merge conflicts resolved

This commit is contained in:
Abhishek Kondur
2023-02-07 14:24:39 +04:00
83 changed files with 1616 additions and 3603 deletions

View File

@@ -0,0 +1,22 @@
package host
import (
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var hostCreateRelayCmd = &cobra.Command{
Use: "create_relay [HOST ID] [RELAYED HOST IDS (comma separated)]",
Args: cobra.ExactArgs(2),
Short: "Turn a Host into a Relay",
Long: `Turn a Host into a Relay`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.CreateRelay(args[0], strings.Split(args[1], ",")))
},
}
func init() {
rootCmd.AddCommand(hostCreateRelayCmd)
}

View File

@@ -0,0 +1,20 @@
package host
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var hostDeleteRelayCmd = &cobra.Command{
Use: "delete_relay [HOST ID]",
Args: cobra.ExactArgs(1),
Short: "Delete Relay role from a host",
Long: `Delete Relay role from a host`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.DeleteRelay(args[0]))
},
}
func init() {
rootCmd.AddCommand(hostDeleteRelayCmd)
}

View File

@@ -38,6 +38,7 @@ var hostUpdateCmd = &cobra.Command{
log.Fatal(err)
}
} else {
apiHost.ID = args[0]
apiHost.EndpointIP = endpoint
apiHost.Name = name
apiHost.ListenPort = listenPort

View File

@@ -41,19 +41,13 @@ var networkCreateCmd = &cobra.Command{
if defaultACL {
network.DefaultACL = "yes"
}
if pointToSite {
network.IsPointToSite = "yes"
}
network.DefaultInterface = defaultInterface
network.DefaultListenPort = int32(defaultListenPort)
network.NodeLimit = int32(nodeLimit)
network.DefaultPostUp = defaultPostUp
network.DefaultPostDown = defaultPostDown
network.DefaultKeepalive = int32(defaultKeepalive)
if allowManualSignUp {
network.AllowManualSignUp = "yes"
}
network.LocalRange = localRange
network.DefaultExtClientDNS = defaultExtClientDNS
network.DefaultMTU = int32(defaultMTU)
}
@@ -70,11 +64,7 @@ func init() {
networkCreateCmd.Flags().BoolVar(&udpHolePunch, "udp_hole_punch", false, "Enable UDP Hole Punching ?")
networkCreateCmd.Flags().BoolVar(&localNetwork, "local", false, "Is the network local (LAN) ?")
networkCreateCmd.Flags().BoolVar(&defaultACL, "default_acl", false, "Enable default Access Control List ?")
networkCreateCmd.Flags().BoolVar(&pointToSite, "point_to_site", false, "Enforce all clients to have only 1 central peer ?")
networkCreateCmd.Flags().StringVar(&defaultInterface, "interface", "", "Name of the network interface")
networkCreateCmd.Flags().StringVar(&defaultPostUp, "post_up", "", "Commands to run after server is up `;` separated")
networkCreateCmd.Flags().StringVar(&defaultPostDown, "post_down", "", "Commands to run after server is down `;` separated")
networkCreateCmd.Flags().StringVar(&localRange, "local_range", "", "Local CIDR range")
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")

View File

@@ -8,15 +8,11 @@ var (
udpHolePunch bool
localNetwork bool
defaultACL bool
pointToSite bool
defaultInterface string
defaultListenPort int
nodeLimit int
defaultPostUp string
defaultPostDown string
defaultKeepalive int
allowManualSignUp bool
localRange string
defaultExtClientDNS string
defaultMTU int
)

View File

@@ -44,19 +44,13 @@ var networkUpdateCmd = &cobra.Command{
if defaultACL {
network.DefaultACL = "yes"
}
if pointToSite {
network.IsPointToSite = "yes"
}
network.DefaultInterface = defaultInterface
network.DefaultListenPort = int32(defaultListenPort)
network.NodeLimit = int32(nodeLimit)
network.DefaultPostUp = defaultPostUp
network.DefaultPostDown = defaultPostDown
network.DefaultKeepalive = int32(defaultKeepalive)
if allowManualSignUp {
network.AllowManualSignUp = "yes"
}
network.LocalRange = localRange
network.DefaultExtClientDNS = defaultExtClientDNS
network.DefaultMTU = int32(defaultMTU)
}
@@ -71,11 +65,7 @@ func init() {
networkUpdateCmd.Flags().BoolVar(&udpHolePunch, "udp_hole_punch", false, "Enable UDP Hole Punching ?")
networkUpdateCmd.Flags().BoolVar(&localNetwork, "local", false, "Is the network local (LAN) ?")
networkUpdateCmd.Flags().BoolVar(&defaultACL, "default_acl", false, "Enable default Access Control List ?")
networkUpdateCmd.Flags().BoolVar(&pointToSite, "point_to_site", false, "Enforce all clients to have only 1 central peer ?")
networkUpdateCmd.Flags().StringVar(&defaultInterface, "interface", "", "Name of the network interface")
networkUpdateCmd.Flags().StringVar(&defaultPostUp, "post_up", "", "Commands to run after server is up `;` separated")
networkUpdateCmd.Flags().StringVar(&defaultPostDown, "post_down", "", "Commands to run after server is down `;` separated")
networkUpdateCmd.Flags().StringVar(&localRange, "local_range", "", "Local CIDR range")
networkUpdateCmd.Flags().StringVar(&defaultExtClientDNS, "ext_client_dns", "", "IPv4 address of DNS server to be used by external clients")
networkUpdateCmd.Flags().IntVar(&defaultListenPort, "listen_port", 0, "Default wireguard port each node will attempt to use")
networkUpdateCmd.Flags().IntVar(&nodeLimit, "node_limit", 0, "Maximum number of nodes that can be associated with this network")

View File

@@ -1,22 +0,0 @@
package node
import (
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var nodeCreateRelayCmd = &cobra.Command{
Use: "create_relay [NETWORK NAME] [NODE ID] [RELAY ADDRESSES (comma separated)]",
Args: cobra.ExactArgs(3),
Short: "Turn a Node into a Relay",
Long: `Turn a Node into a Relay`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.CreateRelay(args[0], args[1], strings.Split(args[2], ",")))
},
}
func init() {
rootCmd.AddCommand(nodeCreateRelayCmd)
}

View File

@@ -1,20 +0,0 @@
package node
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var nodeDeleteRelayCmd = &cobra.Command{
Use: "delete_relay [NETWORK NAME] [NODE ID]",
Args: cobra.ExactArgs(2),
Short: "Delete Relay role from a Node",
Long: `Delete Relay role from a Node`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.DeleteRelay(args[0], args[1]))
},
}
func init() {
rootCmd.AddCommand(nodeDeleteRelayCmd)
}

View File

@@ -34,8 +34,6 @@ var nodeUpdateCmd = &cobra.Command{
node.Address = address
node.Address6 = address6
node.LocalAddress = localAddress
node.PostUp = postUp
node.PostDown = postDown
node.PersistentKeepalive = int32(keepAlive)
if relayAddrs != "" {
node.RelayAddrs = strings.Split(relayAddrs, ",")
@@ -50,6 +48,7 @@ var nodeUpdateCmd = &cobra.Command{
node.DNSOn = dnsOn
node.Connected = !disconnect
}
node.HostID = functions.GetNodeByID(networkName, nodeID).Host.ID.String()
functions.PrettyPrint(functions.UpdateNode(networkName, nodeID, node))
},
}

View File

@@ -1,6 +1,7 @@
package functions
import (
"fmt"
"net/http"
"github.com/gravitl/netmaker/models"
@@ -31,3 +32,16 @@ func UpdateHostNetworks(hostID string, networks []string) *hostNetworksUpdatePay
Networks: networks,
})
}
// CreateRelay - turn a host into a relay
func CreateRelay(hostID string, relayedHosts []string) *models.ApiHost {
return request[models.ApiHost](http.MethodPost, fmt.Sprintf("/api/hosts/%s/relay", hostID), &models.HostRelayRequest{
HostID: hostID,
RelayedHosts: relayedHosts,
})
}
// DeleteRelay - remove relay role from a host
func DeleteRelay(hostID string) *models.ApiHost {
return request[models.ApiHost](http.MethodDelete, fmt.Sprintf("/api/hosts/%s/relay", hostID), nil)
}

View File

@@ -31,20 +31,6 @@ func DeleteNode(networkName, nodeID string) *models.SuccessResponse {
return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s", networkName, nodeID), nil)
}
// CreateRelay - turn a node into a relay
func CreateRelay(networkName, nodeID string, relayAddresses []string) *models.ApiNode {
return request[models.ApiNode](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/createrelay", networkName, nodeID), &models.RelayRequest{
NetID: networkName,
NodeID: nodeID,
RelayAddrs: relayAddresses,
})
}
// DeleteRelay - remove relay role from a node
func DeleteRelay(networkName, nodeID string) *models.ApiNode {
return request[models.ApiNode](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deleterelay", networkName, nodeID), nil)
}
// CreateEgress - turn a node into an egress
func CreateEgress(networkName, nodeID string, payload *models.EgressGatewayRequest) *models.ApiNode {
return request[models.ApiNode](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/creategateway", networkName, nodeID), payload)

View File

@@ -4,15 +4,6 @@ services:
netmaker:
container_name: netmaker
image: gravitl/netmaker:v0.17.1-ee
cap_add:
- NET_ADMIN
- NET_RAW
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv6.conf.all.forwarding=1
restart: always
volumes:
- dnsconfig:/root/config/dnsconfig
@@ -35,10 +26,7 @@ services:
MQ_HOST: "mq"
MQ_PORT: "443"
MQ_SERVER_PORT: "1883"
HOST_NETWORK: "off"
VERBOSITY: "1"
MANAGE_IPTABLES: "on"
PORT_FORWARD_SERVICES: "dns"
METRICS_EXPORTER: "on"
LICENSE_KEY: "YOUR_LICENSE_KEY"
NETMAKER_ACCOUNT_ID: "YOUR_ACCOUNT_ID"

View File

@@ -4,15 +4,6 @@ services:
netmaker: # The Primary Server for running Netmaker
container_name: netmaker
image: gravitl/netmaker:v0.17.1
cap_add:
- NET_ADMIN
- NET_RAW
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv6.conf.all.forwarding=1
restart: always
volumes: # Volume mounts necessary for sql, coredns, and mqtt
- dnsconfig:/root/config/dnsconfig
@@ -26,11 +17,9 @@ services:
COREDNS_ADDR: "SERVER_PUBLIC_IP" # Address of the CoreDNS server. Defaults to SERVER_HOST
DNS_MODE: "on" # Enables DNS Mode, meaning all nodes will set hosts file for private dns settings.
API_PORT: "8081" # The HTTP API port for Netmaker. Used for API calls / communication from front end. If changed, need to change port of BACKEND_URL for netmaker-ui.
CLIENT_MODE: "on" # Depricated. CLIENT_MODE should always be ON
REST_BACKEND: "on" # Enables the REST backend (API running on API_PORT at SERVER_HTTP_HOST). Change to "off" to turn off.
DISABLE_REMOTE_IP_CHECK: "off" # If turned "on", Server will not set Host based on remote IP check. This is already overridden if SERVER_HOST is set. Turned "off" by default.
TELEMETRY: "on" # Whether or not to send telemetry data to help improve Netmaker. Switch to "off" to opt out of sending telemetry.
RCE: "off" # Enables setting PostUp and PostDown (arbitrary commands) on nodes from the server. Off by default.
MASTER_KEY: "REPLACE_MASTER_KEY" # The admin master key for accessing the API. Change this in any production installation.
CORS_ALLOWED_ORIGIN: "*" # The "allowed origin" for API requests. Change to restrict where API requests can come from with comma-separated URLs. ex:- https://dashboard.netmaker.domain1.com,https://dashboard.netmaker.domain2.com
DISPLAY_KEYS: "on" # Show keys permanently in UI (until deleted) as opposed to 1-time display.
@@ -39,10 +28,7 @@ services:
MQ_HOST: "mq" # the address of the mq server. If running from docker compose it will be "mq". Otherwise, need to input address. If using "host networking", it will find and detect the IP of the mq container.
MQ_SERVER_PORT: "1883" # the reachable port of MQ by the server - change if internal MQ port changes (or use external port if MQ is not on the same machine)
MQ_PORT: "443" # the reachable port of MQ - change if external MQ port changes (port on proxy, not necessarily the one exposed in docker-compose)
HOST_NETWORK: "off" # whether or not host networking is turned on. Only turn on if configured for host networking (see docker-compose.hostnetwork.yml). Will set host-level settings like iptables.
VERBOSITY: "1" # logging verbosity level - 1, 2, or 3
MANAGE_IPTABLES: "on" # deprecated
PORT_FORWARD_SERVICES: "dns" # decide which services to port forward ("dns","ssh", or "mq")
# this section is for OAuth
AUTH_PROVIDER: "" # "<azure-ad|github|google|oidc>"
CLIENT_ID: "" # "<client id of your oauth provider>"

View File

@@ -4,15 +4,6 @@ services:
netmaker:
container_name: netmaker
image: gravitl/netmaker:v0.17.1
cap_add:
- NET_ADMIN
- NET_RAW
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv6.conf.all.forwarding=1
restart: always
volumes:
- dnsconfig:/root/config/dnsconfig
@@ -27,7 +18,6 @@ services:
DNS_MODE: "on"
SERVER_HTTP_HOST: "api.NETMAKER_BASE_DOMAIN"
API_PORT: "8081"
CLIENT_MODE: "on"
MASTER_KEY: "REPLACE_MASTER_KEY"
CORS_ALLOWED_ORIGIN: "*"
DISPLAY_KEYS: "on"
@@ -36,7 +26,6 @@ services:
MQ_HOST: "mq"
MQ_PORT: "443"
MQ_SERVER_PORT: "1883"
HOST_NETWORK: "off"
VERBOSITY: "1"
MANAGE_IPTABLES: "on"
PORT_FORWARD_SERVICES: "dns"

View File

@@ -61,10 +61,7 @@ type ServerConfig struct {
FrontendURL string `yaml:"frontendurl"`
DisplayKeys string `yaml:"displaykeys"`
AzureTenant string `yaml:"azuretenant"`
RCE string `yaml:"rce"`
Telemetry string `yaml:"telemetry"`
ManageIPTables string `yaml:"manageiptables"`
PortForwardServices string `yaml:"portforwardservices"`
HostNetwork string `yaml:"hostnetwork"`
MQPort string `yaml:"mqport"`
MQServerPort string `yaml:"mqserverport"`

View File

@@ -160,7 +160,7 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
return
}
entry, err = CreateDNS(entry)
entry, err = logic.CreateDNS(entry)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("Failed to create DNS entry %+v: %v", entry, err))
@@ -223,22 +223,6 @@ func deleteDNS(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(entrytext + " deleted.")
}
// CreateDNS - creates a DNS entry
func CreateDNS(entry models.DNSEntry) (models.DNSEntry, error) {
data, err := json.Marshal(&entry)
if err != nil {
return models.DNSEntry{}, err
}
key, err := logic.GetRecordKey(entry.Name, entry.Network)
if err != nil {
return models.DNSEntry{}, err
}
err = database.Insert(key, string(data), database.DNS_TABLE_NAME)
return entry, err
}
// GetDNSEntry - gets a DNS entry
func GetDNSEntry(domain string, network string) (models.DNSEntry, error) {
var entry models.DNSEntry

View File

@@ -1,35 +1,45 @@
package controller
import (
"net"
"os"
"testing"
"github.com/google/uuid"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/stretchr/testify/assert"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
var dnsHost models.Host
func TestGetAllDNS(t *testing.T) {
database.InitializeDatabase()
deleteAllDNS(t)
deleteAllNetworks()
createNet()
createHost()
t.Run("NoEntries", func(t *testing.T) {
entries, err := logic.GetAllDNS()
assert.Nil(t, err)
assert.Equal(t, []models.DNSEntry(nil), entries)
})
t.Run("OneEntry", func(t *testing.T) {
entry := models.DNSEntry{"10.0.0.3", "", "newhost", "skynet"}
CreateDNS(entry)
entry := models.DNSEntry{
"10.0.0.3", "", "newhost", "skynet",
}
_, err := logic.CreateDNS(entry)
assert.Nil(t, err)
entries, err := logic.GetAllDNS()
assert.Nil(t, err)
assert.Equal(t, 1, len(entries))
})
t.Run("MultipleEntry", func(t *testing.T) {
entry := models.DNSEntry{"10.0.0.7", "", "anotherhost", "skynet"}
CreateDNS(entry)
_, err := logic.CreateDNS(entry)
assert.Nil(t, err)
entries, err := logic.GetAllDNS()
assert.Nil(t, err)
assert.Equal(t, 2, len(entries))
@@ -41,22 +51,42 @@ func TestGetNodeDNS(t *testing.T) {
deleteAllDNS(t)
deleteAllNetworks()
createNet()
createHost()
t.Run("NoNodes", func(t *testing.T) {
dns, err := logic.GetNodeDNS("skynet")
assert.EqualError(t, err, "could not find any records")
assert.Equal(t, []models.DNSEntry(nil), dns)
})
t.Run("NodeExists", func(t *testing.T) {
createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "linux", DNSOn: "yes"}
err := logic.CreateNode(&createnode)
createHost()
_, ipnet, _ := net.ParseCIDR("10.0.0.1/32")
tmpCNode := models.CommonNode{
ID: uuid.New(),
Network: "skynet",
Address: *ipnet,
DNSOn: true,
}
createnode := models.Node{
CommonNode: tmpCNode,
}
err := logic.AssociateNodeToHost(&createnode, &dnsHost)
assert.Nil(t, err)
dns, err := logic.GetNodeDNS("skynet")
assert.Nil(t, err)
assert.Equal(t, "10.0.0.1", dns[0].Address)
})
t.Run("MultipleNodes", func(t *testing.T) {
createnode := &models.LegacyNode{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.100.100.3", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet"}
err := logic.CreateNode(createnode)
_, ipnet, _ := net.ParseCIDR("10.100.100.3/32")
tmpCNode := models.CommonNode{
ID: uuid.New(),
Network: "skynet",
Address: *ipnet,
DNSOn: true,
}
createnode := models.Node{
CommonNode: tmpCNode,
}
err := logic.AssociateNodeToHost(&createnode, &dnsHost)
assert.Nil(t, err)
dns, err := logic.GetNodeDNS("skynet")
assert.Nil(t, err)
@@ -85,15 +115,17 @@ func TestGetCustomDNS(t *testing.T) {
assert.Equal(t, 0, len(dns))
})
t.Run("EntryExist", func(t *testing.T) {
entry := models.DNSEntry{"10.0.0.3", "", "newhost", "skynet"}
CreateDNS(entry)
entry := models.DNSEntry{"10.0.0.3", "", "custom1", "skynet"}
_, err := logic.CreateDNS(entry)
assert.Nil(t, err)
dns, err := logic.GetCustomDNS("skynet")
assert.Nil(t, err)
assert.Equal(t, 1, len(dns))
})
t.Run("MultipleEntries", func(t *testing.T) {
entry := models.DNSEntry{"10.0.0.4", "", "host4", "skynet"}
CreateDNS(entry)
_, err := logic.CreateDNS(entry)
assert.Nil(t, err)
dns, err := logic.GetCustomDNS("skynet")
assert.Nil(t, err)
assert.Equal(t, 2, len(dns))
@@ -112,7 +144,7 @@ func TestGetDNSEntryNum(t *testing.T) {
})
t.Run("NodeExists", func(t *testing.T) {
entry := models.DNSEntry{"10.0.0.2", "", "newhost", "skynet"}
_, err := CreateDNS(entry)
_, err := logic.CreateDNS(entry)
assert.Nil(t, err)
num, err := logic.GetDNSEntryNum("newhost", "skynet")
assert.Nil(t, err)
@@ -131,7 +163,7 @@ func TestGetDNS(t *testing.T) {
})
t.Run("CustomDNSExists", func(t *testing.T) {
entry := models.DNSEntry{"10.0.0.2", "", "newhost", "skynet"}
_, err := CreateDNS(entry)
_, err := logic.CreateDNS(entry)
assert.Nil(t, err)
dns, err := logic.GetDNS("skynet")
t.Log(dns)
@@ -151,7 +183,7 @@ func TestGetDNS(t *testing.T) {
})
t.Run("NodeAndCustomDNS", func(t *testing.T) {
entry := models.DNSEntry{"10.0.0.2", "", "newhost", "skynet"}
_, err := CreateDNS(entry)
_, err := logic.CreateDNS(entry)
assert.Nil(t, err)
dns, err := logic.GetDNS("skynet")
t.Log(dns)
@@ -169,7 +201,7 @@ func TestCreateDNS(t *testing.T) {
deleteAllNetworks()
createNet()
entry := models.DNSEntry{"10.0.0.2", "", "newhost", "skynet"}
dns, err := CreateDNS(entry)
dns, err := logic.CreateDNS(entry)
assert.Nil(t, err)
assert.Equal(t, "newhost", dns.Name)
}
@@ -204,12 +236,13 @@ func TestSetDNS(t *testing.T) {
assert.False(t, info.IsDir())
content, err := os.ReadFile("./config/dnsconfig/netmaker.hosts")
assert.Nil(t, err)
assert.Contains(t, string(content), "testnode.skynet")
assert.Contains(t, string(content), "linuxhost.skynet")
})
t.Run("EntryExists", func(t *testing.T) {
entry := models.DNSEntry{"10.0.0.3", "", "newhost", "skynet"}
CreateDNS(entry)
err := logic.SetDNS()
_, err := logic.CreateDNS(entry)
assert.Nil(t, err)
err = logic.SetDNS()
assert.Nil(t, err)
info, err := os.Stat("./config/dnsconfig/netmaker.hosts")
assert.Nil(t, err)
@@ -228,7 +261,7 @@ func TestGetDNSEntry(t *testing.T) {
createNet()
createTestNode()
entry := models.DNSEntry{"10.0.0.2", "", "newhost", "skynet"}
CreateDNS(entry)
_, _ = logic.CreateDNS(entry)
t.Run("wrong net", func(t *testing.T) {
entry, err := GetDNSEntry("newhost", "w286 Toronto Street South, Uxbridge, ONirecat")
assert.EqualError(t, err, "no result found")
@@ -251,40 +284,13 @@ func TestGetDNSEntry(t *testing.T) {
})
}
// func TestUpdateDNS(t *testing.T) {
// var newentry models.DNSEntry
// database.InitializeDatabase()
// deleteAllDNS(t)
// deleteAllNetworks()
// createNet()
// entry := models.DNSEntry{"10.0.0.2", "newhost", "skynet"}
// CreateDNS(entry)
// t.Run("change address", func(t *testing.T) {
// newentry.Address = "10.0.0.75"
// updated, err := UpdateDNS(newentry, entry)
// assert.Nil(t, err)
// assert.Equal(t, newentry.Address, updated.Address)
// })
// t.Run("change name", func(t *testing.T) {
// newentry.Name = "newname"
// updated, err := UpdateDNS(newentry, entry)
// assert.Nil(t, err)
// assert.Equal(t, newentry.Name, updated.Name)
// })
// t.Run("change network", func(t *testing.T) {
// newentry.Network = "wirecat"
// updated, err := UpdateDNS(newentry, entry)
// assert.Nil(t, err)
// assert.NotEqual(t, newentry.Network, updated.Network)
// })
// }
func TestDeleteDNS(t *testing.T) {
database.InitializeDatabase()
deleteAllDNS(t)
deleteAllNetworks()
createNet()
entry := models.DNSEntry{"10.0.0.2", "", "newhost", "skynet"}
CreateDNS(entry)
_, _ = logic.CreateDNS(entry)
t.Run("EntryExists", func(t *testing.T) {
err := logic.DeleteDNS("newhost", "skynet")
assert.Nil(t, err)
@@ -351,8 +357,8 @@ func TestValidateDNSUpdate(t *testing.T) {
})
t.Run("NameUnique", func(t *testing.T) {
change := models.DNSEntry{"10.0.0.2", "", "myhost", "wirecat"}
CreateDNS(entry)
CreateDNS(change)
_, _ = logic.CreateDNS(entry)
_, _ = logic.CreateDNS(change)
err := logic.ValidateDNSUpdate(change, entry)
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'name_unique' tag")
@@ -401,13 +407,25 @@ func TestValidateDNSCreate(t *testing.T) {
})
t.Run("NameUnique", func(t *testing.T) {
entry := models.DNSEntry{"10.0.0.2", "", "myhost", "skynet"}
_, _ = CreateDNS(entry)
_, _ = logic.CreateDNS(entry)
err := logic.ValidateDNSCreate(entry)
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'name_unique' tag")
})
}
func createHost() {
k, _ := wgtypes.ParseKey("DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=")
dnsHost = models.Host{
ID: uuid.New(),
PublicKey: k.PublicKey(),
HostPass: "password",
OS: "linux",
Name: "dnshost",
}
_ = logic.CreateHost(&dnsHost)
}
func deleteAllDNS(t *testing.T) {
dns, err := logic.GetAllDNS()
assert.Nil(t, err)

View File

@@ -12,12 +12,9 @@ import (
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"golang.org/x/crypto/bcrypt"
)
type hostNetworksUpdatePayload struct {
Networks []string `json:"networks"`
}
func hostHandlers(r *mux.Router) {
r.HandleFunc("/api/hosts", logic.SecurityCheck(true, http.HandlerFunc(getHosts))).Methods(http.MethodGet)
r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(updateHost))).Methods(http.MethodPut)
@@ -26,6 +23,7 @@ func hostHandlers(r *mux.Router) {
r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(deleteHostFromNetwork))).Methods(http.MethodDelete)
r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(createHostRelay))).Methods(http.MethodPost)
r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(deleteHostRelay))).Methods(http.MethodDelete)
r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
}
// swagger:route GET /api/hosts hosts getHosts
@@ -99,6 +97,10 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
if updateRelay {
logic.UpdateHostRelay(currHost.ID.String(), currHost.RelayedHosts, newHost.RelayedHosts)
}
<<<<<<< HEAD
=======
>>>>>>> f4851937c1746475fdac99e9c562623128ba16b1
// publish host update through MQ
if err := mq.HostUpdate(&models.HostUpdate{
Action: models.UpdateHost,
@@ -139,7 +141,33 @@ func deleteHost(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
if currHost.IsRelay {
if _, _, err := logic.DeleteHostRelay(hostid); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to dissociate host from relays:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
}
if currHost.IsRelayed {
relayHost, err := logic.GetHost(currHost.RelayedBy)
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to fetch relay host:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
newRelayedHosts := make([]string, 0)
for _, relayedHostID := range relayHost.RelayedHosts {
if relayedHostID != hostid {
newRelayedHosts = append(newRelayedHosts, relayedHostID)
}
}
relayHost.RelayedHosts = newRelayedHosts
if err := logic.UpsertHost(relayHost); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to update host relays:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
}
if err = logic.RemoveHost(currHost); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
@@ -254,3 +282,96 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
logger.Log(2, r.Header.Get("user"), fmt.Sprintf("removed host %s from network %s", currHost.Name, network))
w.WriteHeader(http.StatusOK)
}
// swagger:route POST /api/hosts/adm/authenticate hosts authenticateHost
//
// Host based authentication for making further API calls.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: successResponse
func authenticateHost(response http.ResponseWriter, request *http.Request) {
var authRequest models.AuthParams
var errorResponse = models.ErrorResponse{
Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
}
decoder := json.NewDecoder(request.Body)
decoderErr := decoder.Decode(&authRequest)
defer request.Body.Close()
if decoderErr != nil {
errorResponse.Code = http.StatusBadRequest
errorResponse.Message = decoderErr.Error()
logger.Log(0, request.Header.Get("user"), "error decoding request body: ",
decoderErr.Error())
logic.ReturnErrorResponse(response, request, errorResponse)
return
}
errorResponse.Code = http.StatusBadRequest
if authRequest.ID == "" {
errorResponse.Message = "W1R3: ID can't be empty"
logger.Log(0, request.Header.Get("user"), errorResponse.Message)
logic.ReturnErrorResponse(response, request, errorResponse)
return
} else if authRequest.Password == "" {
errorResponse.Message = "W1R3: Password can't be empty"
logger.Log(0, request.Header.Get("user"), errorResponse.Message)
logic.ReturnErrorResponse(response, request, errorResponse)
return
}
host, err := logic.GetHost(authRequest.ID)
if err != nil {
errorResponse.Code = http.StatusBadRequest
errorResponse.Message = err.Error()
logger.Log(0, request.Header.Get("user"),
"error retrieving host: ", err.Error())
logic.ReturnErrorResponse(response, request, errorResponse)
return
}
err = bcrypt.CompareHashAndPassword([]byte(host.HostPass), []byte(authRequest.Password))
if err != nil {
errorResponse.Code = http.StatusUnauthorized
errorResponse.Message = "unauthorized"
logger.Log(0, request.Header.Get("user"),
"error validating user password: ", err.Error())
logic.ReturnErrorResponse(response, request, errorResponse)
return
}
tokenString, err := logic.CreateJWT(authRequest.ID, authRequest.MacAddress, "")
if tokenString == "" {
errorResponse.Code = http.StatusUnauthorized
errorResponse.Message = "unauthorized"
logger.Log(0, request.Header.Get("user"),
fmt.Sprintf("%s: %v", errorResponse.Message, err))
logic.ReturnErrorResponse(response, request, errorResponse)
return
}
var successResponse = models.SuccessResponse{
Code: http.StatusOK,
Message: "W1R3: Host " + authRequest.ID + " Authorized",
Response: models.SuccessfulLoginResponse{
AuthToken: tokenString,
ID: authRequest.ID,
},
}
successJSONResponse, jsonError := json.Marshal(successResponse)
if jsonError != nil {
errorResponse.Code = http.StatusBadRequest
errorResponse.Message = err.Error()
logger.Log(0, request.Header.Get("user"),
"error marshalling resp: ", err.Error())
logic.ReturnErrorResponse(response, request, errorResponse)
return
}
response.WriteHeader(http.StatusOK)
response.Header().Set("Content-Type", "application/json")
response.Write(successJSONResponse)
}

78
controllers/migrate.go Normal file
View File

@@ -0,0 +1,78 @@
package controller
import (
"encoding/json"
"io"
"net/http"
"strings"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"golang.org/x/crypto/bcrypt"
)
// swagger:route PUT /api/nodes/{network}/{nodeid}/migrate nodes migrateNode
//
// Used to migrate a legacy node.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeJoinResponse
func migrate(w http.ResponseWriter, r *http.Request) {
// we decode our body request params
data := models.MigrationData{}
err := json.NewDecoder(r.Body).Decode(&data)
if err != nil {
logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
params := mux.Vars(r)
//check authorization
record, err := database.FetchRecord(database.NODES_TABLE_NAME, data.LegacyNodeID)
if err != nil {
logger.Log(0, "no record for legacy node", data.LegacyNodeID, err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
var legacyNode models.LegacyNode
if err = json.Unmarshal([]byte(record), &legacyNode); err != nil {
logger.Log(0, "error decoding legacy node", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
if err := bcrypt.CompareHashAndPassword([]byte(legacyNode.Password), []byte(data.Password)); err != nil {
logger.Log(0, "error decoding legacy password", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
return
}
network, err := logic.GetNetwork(params["network"])
if err != nil {
logger.Log(0, "error retrieving network: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
key, err := logic.CreateAccessKey(models.AccessKey{}, network)
if err != nil {
logger.Log(0, "error creating key: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
data.JoinData.Key = key.Value
payload, err := json.Marshal(data.JoinData)
if err != nil {
logger.Log(0, "error encoding data: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
r.Body = io.NopCloser(strings.NewReader(string(payload)))
r.ContentLength = int64(len(string(payload)))
createNode(w, r)
}

View File

@@ -185,13 +185,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
if !servercfg.GetRce() {
newNetwork.DefaultPostDown = network.DefaultPostDown
newNetwork.DefaultPostUp = network.DefaultPostUp
}
rangeupdate4, rangeupdate6, localrangeupdate, holepunchupdate, groupsDelta, userDelta, err := logic.UpdateNetwork(&network, &newNetwork)
rangeupdate4, rangeupdate6, holepunchupdate, groupsDelta, userDelta, err := logic.UpdateNetwork(&network, &newNetwork)
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to update network: ",
err.Error())
@@ -237,17 +231,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
return
}
}
if localrangeupdate {
err = logic.UpdateNetworkLocalAddresses(network.NetID)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to update network [%s] local addresses: %v",
network.NetID, err.Error()))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
}
if rangeupdate4 || rangeupdate6 || localrangeupdate || holepunchupdate {
if rangeupdate4 || rangeupdate6 || holepunchupdate {
nodes, err := logic.GetNetworkNodes(network.NetID)
if err != nil {
logger.Log(0, r.Header.Get("user"),

View File

@@ -4,10 +4,12 @@ import (
"os"
"testing"
"github.com/google/uuid"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/stretchr/testify/assert"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
type NetworkValidationTestCase struct {
@@ -16,6 +18,8 @@ type NetworkValidationTestCase struct {
errMessage string
}
var netHost models.Host
func TestCreateNetwork(t *testing.T) {
initialize()
deleteAllNetworks()
@@ -271,14 +275,6 @@ func TestValidateNetwork(t *testing.T) {
},
errMessage: "Field validation for 'DefaultKeepalive' failed on the 'max' tag",
},
{
testname: "InvalidLocalRange",
network: models.Network{
NetID: "skynet",
LocalRange: "192.168.0.1",
},
errMessage: "Field validation for 'LocalRange' failed on the 'cidr' tag",
},
}
for _, tc := range cases {
t.Run(tc.testname, func(t *testing.T) {
@@ -305,11 +301,12 @@ func TestIpv6Network(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, network.AddressRange6, "fde6:be04:fa5e:d076::/64")
})
node1 := models.LegacyNode{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.50", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet6", OS: "linux"}
nodeErr := logic.CreateNode(&node1)
node1 := createNodeWithParams("skynet6", "")
createNetHost()
nodeErr := logic.AssociateNodeToHost(node1, &netHost)
t.Run("Test node on network IPv6", func(t *testing.T) {
assert.Nil(t, nodeErr)
assert.Equal(t, "fde6:be04:fa5e:d076::1", node1.Address6)
assert.Equal(t, "fde6:be04:fa5e:d076::1", node1.Address6.IP.String())
})
}
@@ -358,3 +355,15 @@ func createNetDualStack() {
logic.CreateNetwork(network)
}
}
func createNetHost() {
k, _ := wgtypes.ParseKey("DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=")
netHost = models.Host{
ID: uuid.New(),
PublicKey: k.PublicKey(),
HostPass: "password",
OS: "linux",
Name: "nethost",
}
_ = logic.CreateHost(&netHost)
}

View File

@@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strings"
@@ -26,7 +25,7 @@ func nodeHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}", authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPut)
r.HandleFunc("/api/nodes/{network}/{nodeid}/migrate", authorize(true, true, "node", http.HandlerFunc(migrate))).Methods(http.MethodPut)
r.HandleFunc("/api/nodes/{network}/{nodeid}/migrate", migrate).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", authorize(false, true, "user", http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete)
@@ -188,6 +187,7 @@ func nodeauth(next http.Handler) http.HandlerFunc {
logic.ReturnErrorResponse(w, r, errorResponse)
return
}
next.ServeHTTP(w, r)
}
}
@@ -524,7 +524,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
}
if !logic.IsVersionComptatible(data.Host.Version) {
err := errors.New("incomatible netclient version")
err := errors.New("incompatible netclient version")
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
@@ -550,6 +550,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, errorResponse)
return
}
logic.DecrimentKey(networkName, data.Key)
user, err := pro.GetNetworkUser(networkName, promodels.NetworkUserID(keyName))
if err == nil {
if user.ID != "" {
@@ -577,6 +578,9 @@ func createNode(w http.ResponseWriter, r *http.Request) {
server := servercfg.GetServerInfo()
server.TrafficKey = key
data.Node.Server = servercfg.GetServer()
if !logic.HostExists(&data.Host) {
logic.CheckHostPorts(&data.Host)
}
if err := logic.CreateHost(&data.Host); err != nil {
if errors.Is(err, logic.ErrHostExists) {
logger.Log(3, "host exists .. no need to create")
@@ -586,8 +590,15 @@ func createNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
logic.UpdateHost(&data.Host, host) // update the in memory struct values
logic.UpdateHostFromClient(&data.Host, host) // update the in memory struct values
err = logic.UpsertHost(host)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to update host [ %s ]: %v", host.ID.String(), err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
data.Host = *host
} else {
logger.Log(0, "error creating host", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
@@ -628,6 +639,8 @@ func createNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
data.Host.HostPass = "" // client should not change password after join
// concealing hash
response := models.NodeJoinResponse{
Node: data.Node,
ServerConfig: server,
@@ -660,32 +673,33 @@ func createNode(w http.ResponseWriter, r *http.Request) {
// Responses:
// 200: nodeResponse
func createEgressGateway(w http.ResponseWriter, r *http.Request) {
var gateway models.EgressGatewayRequest
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
err := json.NewDecoder(r.Body).Decode(&gateway)
if err != nil {
logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
gateway.NetID = params["network"]
gateway.NodeID = params["nodeid"]
node, err := logic.CreateEgressGateway(gateway)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to create egress gateway on node [%s] on network [%s]: %v",
gateway.NodeID, gateway.NetID, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "created egress gateway on node", gateway.NodeID, "on network", gateway.NetID)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
runUpdates(&node, true)
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("currently unimplemented"), "internal"))
// var gateway models.EgressGatewayRequest
// var params = mux.Vars(r)
// w.Header().Set("Content-Type", "application/json")
// err := json.NewDecoder(r.Body).Decode(&gateway)
// if err != nil {
// logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
// logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
// return
// }
// gateway.NetID = params["network"]
// gateway.NodeID = params["nodeid"]
// node, err := logic.CreateEgressGateway(gateway)
// if err != nil {
// logger.Log(0, r.Header.Get("user"),
// fmt.Sprintf("failed to create egress gateway on node [%s] on network [%s]: %v",
// gateway.NodeID, gateway.NetID, err))
// logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
// return
// }
//
// apiNode := node.ConvertToAPINode()
// logger.Log(1, r.Header.Get("user"), "created egress gateway on node", gateway.NodeID, "on network", gateway.NetID)
// w.WriteHeader(http.StatusOK)
// json.NewEncoder(w).Encode(apiNode)
//
// runUpdates(&node, true)
}
// swagger:route DELETE /api/nodes/{network}/{nodeid}/deletegateway nodes deleteEgressGateway
@@ -700,25 +714,26 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
// Responses:
// 200: nodeResponse
func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
nodeid := params["nodeid"]
netid := params["network"]
node, err := logic.DeleteEgressGateway(netid, nodeid)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to delete egress gateway on node [%s] on network [%s]: %v",
nodeid, netid, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "deleted egress gateway on node", nodeid, "on network", netid)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
runUpdates(&node, true)
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("currently unimplemented"), "internal"))
//w.Header().Set("Content-Type", "application/json")
// var params = mux.Vars(r)
// nodeid := params["nodeid"]
// netid := params["network"]
// node, err := logic.DeleteEgressGateway(netid, nodeid)
// if err != nil {
// logger.Log(0, r.Header.Get("user"),
// fmt.Sprintf("failed to delete egress gateway on node [%s] on network [%s]: %v",
// nodeid, netid, err))
// logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
// return
// }
//
// apiNode := node.ConvertToAPINode()
// logger.Log(1, r.Header.Get("user"), "deleted egress gateway on node", nodeid, "on network", netid)
// w.WriteHeader(http.StatusOK)
// json.NewEncoder(w).Encode(apiNode)
//
// runUpdates(&node, true)
}
// == INGRESS ==
@@ -807,51 +822,6 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
runUpdates(&node, true)
}
// swagger:route PUT /api/nodes/{network}/{nodeid}/migrate nodes migrateNode
//
// Used to migrate a legacy node.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeJoinResponse
func migrate(w http.ResponseWriter, r *http.Request) {
// we decode our body request params
data := models.JoinData{}
err := json.NewDecoder(r.Body).Decode(&data)
if err != nil {
logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
params := mux.Vars(r)
network, err := logic.GetNetwork(params["network"])
if err != nil {
logger.Log(0, "error retrieving network: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
key, err := logic.CreateAccessKey(models.AccessKey{}, network)
if err != nil {
logger.Log(0, "error creating key: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
data.Key = key.Value
payload, err := json.Marshal(data)
if err != nil {
logger.Log(0, "error encoding data: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
r.Body = io.NopCloser(strings.NewReader(string(payload)))
r.ContentLength = int64(len(string(payload)))
createNode(w, r)
}
// swagger:route PUT /api/nodes/{network}/{nodeid} nodes updateNode
//
// Update an individual node.
@@ -920,11 +890,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
if currentNode.IsRelayed && (currentNode.Address.String() != newNode.Address.String() || currentNode.Address6.String() != newNode.Address6.String()) {
relayedUpdate = true
}
if !servercfg.GetRce() {
newNode.PostDown = currentNode.PostDown
newNode.PostUp = currentNode.PostUp
}
ifaceDelta := logic.IfaceDelta(&currentNode, newNode)
if ifaceDelta && servercfg.Is_EE {
@@ -988,7 +953,6 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
if r.Header.Get("ismaster") != "yes" {
username := r.Header.Get("user")
if username != "" && !doesUserOwnNode(username, params["network"], nodeid) {
@@ -1002,9 +966,7 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
}
logic.ReturnSuccessResponse(w, r, nodeid+" deleted.")
logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
if !fromNode {
// notify node change
if !fromNode { // notify node change
runUpdates(&node, false)
}
go func() { // notify of peer change

View File

@@ -1,16 +1,22 @@
package controller
import (
"net"
"testing"
"github.com/google/uuid"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/logic/acls"
"github.com/gravitl/netmaker/logic/acls/nodeacls"
"github.com/gravitl/netmaker/models"
"github.com/stretchr/testify/assert"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
var nonLinuxHost models.Host
var linuxHost models.Host
func TestCreateEgressGateway(t *testing.T) {
var gateway models.EgressGatewayRequest
gateway.Interface = "eth0"
@@ -21,45 +27,39 @@ func TestCreateEgressGateway(t *testing.T) {
createNet()
t.Run("NoNodes", func(t *testing.T) {
node, err := logic.CreateEgressGateway(gateway)
assert.Equal(t, models.LegacyNode{}, node)
assert.Equal(t, models.Node{}, node)
assert.EqualError(t, err, "could not find any records")
})
t.Run("Non-linux node", func(t *testing.T) {
createnode := models.LegacyNode{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "windows"}
err := logic.CreateNode(&createnode)
createnode := createNodeWithParams("", "")
createNodeHosts()
createnode.HostID = nonLinuxHost.ID
err := logic.AssociateNodeToHost(createnode, &nonLinuxHost)
assert.Nil(t, err)
gateway.NodeID = createnode.ID
gateway.NodeID = createnode.ID.String()
node, err := logic.CreateEgressGateway(gateway)
assert.Equal(t, models.LegacyNode{}, node)
assert.Equal(t, models.Node{}, node)
assert.EqualError(t, err, "windows is unsupported for egress gateways")
})
t.Run("Success-Nat-Enabled", func(t *testing.T) {
deleteAllNodes()
testnode := createTestNode()
gateway.NodeID = testnode.ID
gateway.NodeID = testnode.ID.String()
gateway.NatEnabled = "yes"
node, err := logic.CreateEgressGateway(gateway)
t.Log(node.EgressGatewayNatEnabled)
t.Log(node.PostUp)
t.Log(node.PostDown)
assert.Nil(t, err)
assert.Contains(t, node.PostUp, "-j MASQUERADE")
assert.Contains(t, node.PostDown, "-j MASQUERADE")
})
t.Run("Success-Nat-Disabled", func(t *testing.T) {
deleteAllNodes()
testnode := createTestNode()
gateway.NodeID = testnode.ID
gateway.NodeID = testnode.ID.String()
gateway.NatEnabled = "no"
node, err := logic.CreateEgressGateway(gateway)
t.Log(node.EgressGatewayNatEnabled)
t.Log(node.PostUp)
t.Log(node.PostDown)
assert.Nil(t, err)
assert.NotContains(t, node.PostUp, "-j MASUERADE")
assert.NotContains(t, node.PostDown, "-j MASUERADE")
})
t.Run("Success", func(t *testing.T) {
var gateway models.EgressGatewayRequest
@@ -68,14 +68,12 @@ func TestCreateEgressGateway(t *testing.T) {
gateway.NetID = "skynet"
deleteAllNodes()
testnode := createTestNode()
gateway.NodeID = testnode.ID
gateway.NodeID = testnode.ID.String()
node, err := logic.CreateEgressGateway(gateway)
t.Log(node)
assert.Nil(t, err)
assert.Contains(t, node.PostUp, "-j MASQUERADE")
assert.Contains(t, node.PostDown, "-j MASQUERADE")
assert.Equal(t, "yes", node.IsEgressGateway)
assert.Equal(t, true, node.IsEgressGateway)
assert.Equal(t, gateway.Ranges, node.EgressGatewayRanges)
})
@@ -89,31 +87,27 @@ func TestDeleteEgressGateway(t *testing.T) {
gateway.Interface = "eth0"
gateway.Ranges = []string{"10.100.100.0/24"}
gateway.NetID = "skynet"
gateway.NodeID = testnode.ID
gateway.NodeID = testnode.ID.String()
t.Run("Success", func(t *testing.T) {
node, err := logic.CreateEgressGateway(gateway)
assert.Nil(t, err)
assert.Equal(t, "yes", node.IsEgressGateway)
assert.Equal(t, true, node.IsEgressGateway)
assert.Equal(t, []string{"10.100.100.0/24"}, node.EgressGatewayRanges)
node, err = logic.DeleteEgressGateway(gateway.NetID, gateway.NodeID)
assert.Nil(t, err)
assert.Equal(t, "no", node.IsEgressGateway)
assert.Equal(t, false, node.IsEgressGateway)
assert.Equal(t, []string([]string{}), node.EgressGatewayRanges)
assert.Equal(t, "", node.PostUp)
assert.Equal(t, "", node.PostDown)
})
t.Run("NotGateway", func(t *testing.T) {
node, err := logic.DeleteEgressGateway(gateway.NetID, gateway.NodeID)
assert.Nil(t, err)
assert.Equal(t, "no", node.IsEgressGateway)
assert.Equal(t, false, node.IsEgressGateway)
assert.Equal(t, []string([]string{}), node.EgressGatewayRanges)
assert.Equal(t, "", node.PostUp)
assert.Equal(t, "", node.PostDown)
})
t.Run("BadNode", func(t *testing.T) {
node, err := logic.DeleteEgressGateway(gateway.NetID, "01:02:03")
assert.EqualError(t, err, "no result found")
assert.Equal(t, models.LegacyNode{}, node)
assert.Equal(t, models.Node{}, node)
deleteAllNodes()
})
}
@@ -140,23 +134,7 @@ func TestGetNetworkNodes(t *testing.T) {
})
}
func TestUncordonNode(t *testing.T) {
database.InitializeDatabase()
deleteAllNetworks()
createNet()
node := createTestNode()
t.Run("BadID", func(t *testing.T) {
resp, err := logic.UncordonNode("blahblah")
assert.Equal(t, models.LegacyNode{}, resp)
assert.EqualError(t, err, "no result found")
})
t.Run("Success", func(t *testing.T) {
resp, err := logic.UncordonNode(node.ID)
assert.Nil(t, err)
assert.Equal(t, "no", resp.IsPending)
})
}
func TestValidateEgressGateway(t *testing.T) {
var gateway models.EgressGatewayRequest
t.Run("EmptyRange", func(t *testing.T) {
@@ -181,66 +159,73 @@ func TestValidateEgressGateway(t *testing.T) {
func TestNodeACLs(t *testing.T) {
deleteAllNodes()
node1 := models.LegacyNode{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.50", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "linux"}
node2 := models.LegacyNode{PublicKey: "DM5qhLAE20FG7BbfBCger+Ac9D2NDOwCtY1rbYDXf14=", Name: "testnode", Endpoint: "10.0.0.100", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet", OS: "linux"}
logic.CreateNode(&node1)
logic.CreateNode(&node2)
node1 := createNodeWithParams("", "10.0.0.50/32")
node2 := createNodeWithParams("", "10.0.0.100/32")
logic.AssociateNodeToHost(node1, &linuxHost)
logic.AssociateNodeToHost(node2, &linuxHost)
t.Run("acls not present", func(t *testing.T) {
currentACL, err := nodeacls.FetchAllACLs(nodeacls.NetworkID(node1.Network))
assert.Nil(t, err)
assert.NotNil(t, currentACL)
node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID))
node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID.String()))
assert.Nil(t, err)
assert.NotNil(t, node1ACL)
assert.Equal(t, acls.Allowed, node1ACL[acls.AclID(node2.ID)])
assert.Equal(t, acls.Allowed, node1ACL[acls.AclID(node2.ID.String())])
})
t.Run("node acls exists after creates", func(t *testing.T) {
node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID))
node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID.String()))
assert.Nil(t, err)
assert.NotNil(t, node1ACL)
node2ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node2.Network), nodeacls.NodeID(node2.ID))
node2ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node2.Network), nodeacls.NodeID(node2.ID.String()))
assert.Nil(t, err)
assert.NotNil(t, node2ACL)
assert.Equal(t, acls.Allowed, node2ACL[acls.AclID(node1.ID)])
assert.Equal(t, acls.Allowed, node2ACL[acls.AclID(node1.ID.String())])
})
t.Run("node acls correct after fetch", func(t *testing.T) {
node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID))
node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID.String()))
assert.Nil(t, err)
assert.Equal(t, acls.Allowed, node1ACL[acls.AclID(node2.ID)])
assert.Equal(t, acls.Allowed, node1ACL[acls.AclID(node2.ID.String())])
})
t.Run("node acls correct after modify", func(t *testing.T) {
node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID))
node1ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID.String()))
assert.Nil(t, err)
assert.NotNil(t, node1ACL)
node2ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node2.Network), nodeacls.NodeID(node2.ID))
node2ACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(node2.Network), nodeacls.NodeID(node2.ID.String()))
assert.Nil(t, err)
assert.NotNil(t, node2ACL)
currentACL, err := nodeacls.DisallowNodes(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID), nodeacls.NodeID(node2.ID))
currentACL, err := nodeacls.DisallowNodes(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID.String()), nodeacls.NodeID(node2.ID.String()))
assert.Nil(t, err)
assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node1.ID)][acls.AclID(node2.ID)])
assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node2.ID)][acls.AclID(node1.ID)])
assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node1.ID.String())][acls.AclID(node2.ID.String())])
assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node2.ID.String())][acls.AclID(node1.ID.String())])
currentACL.Save(acls.ContainerID(node1.Network))
})
t.Run("node acls correct after add new node not allowed", func(t *testing.T) {
node3 := models.LegacyNode{PublicKey: "this-is-not-valid", Name: "testnode3", Endpoint: "10.0.0.100", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet", OS: "linux"}
logic.CreateNode(&node3)
var currentACL, err = nodeacls.FetchAllACLs(nodeacls.NetworkID(node3.Network))
node3 := createNodeWithParams("", "10.0.0.100/32")
createNodeHosts()
n, e := logic.GetNetwork(node3.Network)
assert.Nil(t, e)
n.DefaultACL = "no"
e = logic.SaveNetwork(&n)
assert.Nil(t, e)
err := logic.AssociateNodeToHost(node3, &linuxHost)
assert.Nil(t, err)
currentACL, err := nodeacls.FetchAllACLs(nodeacls.NetworkID(node3.Network))
assert.Nil(t, err)
assert.NotNil(t, currentACL)
assert.Equal(t, acls.NotPresent, currentACL[acls.AclID(node1.ID)][acls.AclID(node3.ID)])
nodeACL, err := nodeacls.CreateNodeACL(nodeacls.NetworkID(node3.Network), nodeacls.NodeID(node3.ID), acls.NotAllowed)
assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node1.ID.String())][acls.AclID(node3.ID.String())])
nodeACL, err := nodeacls.CreateNodeACL(nodeacls.NetworkID(node3.Network), nodeacls.NodeID(node3.ID.String()), acls.NotAllowed)
assert.Nil(t, err)
nodeACL.Save(acls.ContainerID(node3.Network), acls.AclID(node3.ID))
nodeACL.Save(acls.ContainerID(node3.Network), acls.AclID(node3.ID.String()))
currentACL, err = nodeacls.FetchAllACLs(nodeacls.NetworkID(node3.Network))
assert.Nil(t, err)
assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node1.ID)][acls.AclID(node3.ID)])
assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node2.ID)][acls.AclID(node3.ID)])
assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node1.ID.String())][acls.AclID(node3.ID.String())])
assert.Equal(t, acls.NotAllowed, currentACL[acls.AclID(node2.ID.String())][acls.AclID(node3.ID.String())])
})
t.Run("node acls removed", func(t *testing.T) {
retNetworkACL, err := nodeacls.RemoveNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID))
retNetworkACL, err := nodeacls.RemoveNodeACL(nodeacls.NetworkID(node1.Network), nodeacls.NodeID(node1.ID.String()))
assert.Nil(t, err)
assert.NotNil(t, retNetworkACL)
assert.Equal(t, acls.NotPresent, retNetworkACL[acls.AclID(node2.ID)][acls.AclID(node1.ID)])
assert.Equal(t, acls.NotPresent, retNetworkACL[acls.AclID(node2.ID.String())][acls.AclID(node1.ID.String())])
})
deleteAllNodes()
}
@@ -249,8 +234,51 @@ func deleteAllNodes() {
database.DeleteAllRecords(database.NODES_TABLE_NAME)
}
func createTestNode() *models.LegacyNode {
createnode := models.LegacyNode{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Name: "testnode", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet", OS: "linux"}
logic.CreateNode(&createnode)
func createTestNode() *models.Node {
createNodeHosts()
n := createNodeWithParams("skynet", "")
_ = logic.AssociateNodeToHost(n, &linuxHost)
return n
}
func createNodeWithParams(network, address string) *models.Node {
_, ipnet, _ := net.ParseCIDR("10.0.0.1/32")
tmpCNode := models.CommonNode{
ID: uuid.New(),
Network: "skynet",
Address: *ipnet,
DNSOn: true,
}
if len(network) > 0 {
tmpCNode.Network = network
}
if len(address) > 0 {
_, ipnet2, _ := net.ParseCIDR(address)
tmpCNode.Address = *ipnet2
}
createnode := models.Node{
CommonNode: tmpCNode,
}
return &createnode
}
func createNodeHosts() {
k, _ := wgtypes.ParseKey("DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=")
linuxHost = models.Host{
ID: uuid.New(),
PublicKey: k.PublicKey(),
HostPass: "password",
OS: "linux",
Name: "linuxhost",
}
_ = logic.CreateHost(&linuxHost)
nonLinuxHost = models.Host{
ID: uuid.New(),
OS: "windows",
PublicKey: k.PublicKey(),
Name: "windowshost",
HostPass: "password",
}
_ = logic.CreateHost(&nonLinuxHost)
}

View File

@@ -32,7 +32,6 @@ server:
frontendurl: ""
displaykeys: ""
azuretenant: ""
rce: "off"
telemetry: ""
manageiptables: "off"
portforwardservices: ""

View File

@@ -23,6 +23,7 @@ func ResetFailover(network string) error {
return err
}
for _, node := range nodes {
node := node
err = SetFailover(&node)
if err != nil {
logger.Log(2, "error setting failover for node", node.ID.String(), ":", err.Error())

6
go.mod
View File

@@ -20,7 +20,7 @@ require (
golang.org/x/oauth2 v0.3.0
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c
golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1
@@ -29,7 +29,7 @@ require (
require (
filippo.io/edwards25519 v1.0.0
github.com/c-robinson/iplib v1.0.6
github.com/go-ping/ping v1.1.0 // indirect
github.com/go-ping/ping v1.1.0
github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0
)
@@ -37,13 +37,11 @@ require (
github.com/coreos/go-oidc/v3 v3.5.0
github.com/gorilla/websocket v1.5.0
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.0
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
gortc.io/stun v1.23.0
)
require (
github.com/gravitl/netclient v0.0.0-20230114051017-65ecaeffca09
github.com/guumaster/tablewriter v0.0.10
github.com/matryer/is v1.4.0
github.com/olekukonko/tablewriter v0.0.5

9
go.sum
View File

@@ -62,8 +62,6 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gravitl/netclient v0.0.0-20230114051017-65ecaeffca09 h1:T0gLl+i8whnrdwtW91R4u8x8bmqFVfPTU9WfBratkMc=
github.com/gravitl/netclient v0.0.0-20230114051017-65ecaeffca09/go.mod h1:g3q+vhLySW/6smOsWsVy5LrxoW++f+kqiBAp9BM6sbY=
github.com/guumaster/tablewriter v0.0.10 h1:A0HD94yMdt4usgxBjoEceNeE0XMJ027euoHAzsPqBQs=
github.com/guumaster/tablewriter v0.0.10/go.mod h1:p4FRFhyfo0UD9ZLmMRbbJooTUsxo6b80qZTERVDWrH8=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
@@ -76,8 +74,8 @@ github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTx
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -119,16 +117,14 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f h1:BSnJgAfHzEp7o8PYJ7YfwAVHhqu7BYUTggcn/LGlUWY=
github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f/go.mod h1:UW/gxgQwSePTvL1KA8QEHsXeYHP4xkoXgbDdN781p34=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
@@ -208,7 +204,6 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@@ -40,10 +40,6 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models
return models.AccessKey{}, errors.New("duplicate AccessKey Name")
}
}
privAddr := ""
if network.IsLocal != "" {
privAddr = network.LocalRange
}
netID := network.NetID
@@ -52,7 +48,6 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models
accessToken.APIConnString = servercfg.GetAPIConnString()
accessToken.ClientConfig.Network = netID
accessToken.ClientConfig.Key = accesskey.Value
accessToken.ClientConfig.LocalRange = privAddr
tokenjson, err := json.Marshal(accessToken)
if err != nil {

View File

@@ -74,14 +74,27 @@ func GetNodeDNS(network string) ([]models.DNSEntry, error) {
}
for _, value := range collection {
var entry models.DNSEntry
var node models.Node
if err = json.Unmarshal([]byte(value), &node); err != nil {
continue
}
if err = json.Unmarshal([]byte(value), &entry); node.Network == network && err == nil {
dns = append(dns, entry)
if node.Network != network {
continue
}
host, err := GetHost(node.HostID.String())
if err != nil {
continue
}
var entry = models.DNSEntry{}
entry.Name = host.Name
entry.Network = network
if node.Address.IP != nil {
entry.Address = node.Address.IP.String()
}
if node.Address6.IP != nil {
entry.Address6 = node.Address6.IP.String()
}
dns = append(dns, entry)
}
return dns, nil
@@ -220,9 +233,6 @@ func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
})
_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
_, err := GetParentNetwork(change.Network)
if err != nil {
logger.Log(0, err.Error())
}
return err == nil
})
@@ -245,3 +255,20 @@ func DeleteDNS(domain string, network string) error {
err = database.DeleteRecord(database.DNS_TABLE_NAME, key)
return err
}
// CreateDNS - creates a DNS entry
func CreateDNS(entry models.DNSEntry) (models.DNSEntry, error) {
k, err := GetRecordKey(entry.Name, entry.Network)
if err != nil {
return models.DNSEntry{}, err
}
data, err := json.Marshal(&entry)
if err != nil {
return models.DNSEntry{}, err
}
err = database.Insert(k, string(data), database.DNS_TABLE_NAME)
return entry, err
}

View File

@@ -138,12 +138,6 @@ func CreateExtClient(extclient *models.ExtClient) error {
return err
}
extclient.Address = newAddress.String()
extclientInternalAddr, err := UniqueAddress(extclient.Network, false)
if err != nil {
return err
}
extclient.InternalIPAddr = extclientInternalAddr.String()
}
}
@@ -154,11 +148,6 @@ func CreateExtClient(extclient *models.ExtClient) error {
return err
}
extclient.Address6 = addr6.String()
extclientInternalAddr6, err := UniqueAddress6(extclient.Network, false)
if err != nil {
return err
}
extclient.InternalIPAddr6 = extclientInternalAddr6.String()
}
}

View File

@@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"github.com/gravitl/netmaker/database"
@@ -48,62 +47,6 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
node.EgressGatewayRanges = gateway.Ranges
node.EgressGatewayNatEnabled = models.ParseBool(gateway.NatEnabled)
node.EgressGatewayRequest = gateway // store entire request for use when preserving the egress gateway
postUpCmd := ""
postDownCmd := ""
ipv4, ipv6 := getNetworkProtocols(gateway.Ranges)
logger.Log(3, "creating egress gateway firewall in use is '", host.FirewallInUse, "'")
iface := models.WIREGUARD_INTERFACE
if host.OS == "linux" {
switch host.FirewallInUse {
case models.FIREWALL_NFTABLES:
// nftables only supported on Linux
// assumes chains eg FORWARD and postrouting already exist
logger.Log(3, "creating egress gateway nftables is present")
// down commands don't remove as removal of the rules leaves an empty chain while
// removing the chain with rules in it would remove all rules in that section (not safe
// if there are remaining rules on the host that need to stay). In practice the chain is removed
// when non-empty even though the removal of a non-empty chain should not be possible per nftables wiki.
postUpCmd, postDownCmd = firewallNFTCommandsCreateEgress(iface, gateway.Interface, gateway.Ranges, node.EgressGatewayNatEnabled, ipv4, ipv6)
default: // iptables assumed
logger.Log(3, "creating egress gateway nftables is not present")
postUpCmd, postDownCmd = firewallIPTablesCommandsCreateEgress(iface, gateway.Interface, node.EgressGatewayNatEnabled, ipv4, ipv6)
}
}
if host.OS == "freebsd" {
// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
postUpCmd = "kldload ipfw ipfw_nat ; "
postUpCmd += "ipfw disable one_pass ; "
postUpCmd += "ipfw nat 1 config if " + gateway.Interface + " same_ports unreg_only reset ; "
postUpCmd += "ipfw add 64000 reass all from any to any in ; "
postUpCmd += "ipfw add 64000 nat 1 ip from any to any in via " + gateway.Interface + " ; "
postUpCmd += "ipfw add 64000 check-state ; "
postUpCmd += "ipfw add 64000 nat 1 ip from any to any out via " + gateway.Interface + " ; "
postUpCmd += "ipfw add 65534 allow ip from any to any ; "
postDownCmd = "ipfw delete 64000 ; "
postDownCmd += "ipfw delete 65534 ; "
postDownCmd += "kldunload ipfw_nat ipfw"
}
if gateway.PostUp != "" {
postUpCmd = gateway.PostUp
}
if gateway.PostDown != "" {
postDownCmd = gateway.PostDown
}
if node.PostUp != "" {
if !strings.Contains(node.PostUp, postUpCmd) {
postUpCmd = node.PostUp + postUpCmd
}
}
if node.PostDown != "" {
if !strings.Contains(node.PostDown, postDownCmd) {
postDownCmd = node.PostDown + postDownCmd
}
}
node.PostUp = postUpCmd
node.PostDown = postDownCmd
node.SetLastModified()
nodeData, err := json.Marshal(&node)
if err != nil {
@@ -136,39 +79,9 @@ func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
if err != nil {
return models.Node{}, err
}
host, err := GetHost(node.HostID.String())
if err != nil {
return models.Node{}, err
}
node.IsEgressGateway = false
node.EgressGatewayRanges = []string{}
node.EgressGatewayRequest = models.EgressGatewayRequest{} // remove preserved request as the egress gateway is gone
// needed in case we don't preserve a gateway (i.e., no ingress to preserve)
node.PostUp = ""
node.PostDown = ""
cidrs := []string{}
cidrs = append(cidrs, node.IngressGatewayRange)
cidrs = append(cidrs, node.IngressGatewayRange6)
ipv4, ipv6 := getNetworkProtocols(cidrs)
logger.Log(3, "deleting egress gateway firewall in use is '", host.FirewallInUse, "'")
if node.IsIngressGateway { // check if node is still an ingress gateway before completely deleting postdown/up rules
// still have an ingress gateway so preserve it
iface := models.WIREGUARD_INTERFACE
if host.OS == "linux" {
switch host.FirewallInUse {
case models.FIREWALL_NFTABLES:
// nftables only supported on Linux
// assumes chains eg FORWARD and postrouting already exist
logger.Log(3, "deleting egress gateway nftables is present")
node.PostUp, node.PostDown = firewallNFTCommandsCreateIngress(iface)
default:
logger.Log(3, "deleting egress gateway nftables is not present")
node.PostUp, node.PostDown = firewallIPTablesCommandsCreateIngress(iface, ipv4, ipv6)
}
}
// no need to preserve ingress gateway on FreeBSD as ingress is not supported on that OS
}
node.SetLastModified()
data, err := json.Marshal(&node)
@@ -184,7 +97,6 @@ func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
// CreateIngressGateway - creates an ingress gateway
func CreateIngressGateway(netid string, nodeid string, failover bool) (models.Node, error) {
var postUpCmd, postDownCmd string
node, err := GetNodeByID(nodeid)
if err != nil {
return models.Node{}, err
@@ -193,6 +105,9 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.No
if err != nil {
return models.Node{}, err
}
if host.OS != "linux" {
return models.Node{}, errors.New("ingress can only be created on linux based node")
}
if host.FirewallInUse == models.FIREWALL_NONE {
return models.Node{}, errors.New("firewall is not supported for ingress gateways")
}
@@ -202,38 +117,9 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.No
return models.Node{}, err
}
node.IsIngressGateway = true
cidrs := []string{}
cidrs = append(cidrs, network.AddressRange)
cidrs = append(cidrs, network.AddressRange6)
node.IngressGatewayRange = network.AddressRange
node.IngressGatewayRange6 = network.AddressRange6
ipv4, ipv6 := getNetworkProtocols(cidrs)
logger.Log(3, "creating ingress gateway firewall in use is '", host.FirewallInUse, "'")
iface := models.WIREGUARD_INTERFACE
switch host.FirewallInUse {
case models.FIREWALL_NFTABLES:
// nftables only supported on Linux
// assumes chains eg FORWARD and postrouting already exist
logger.Log(3, "creating ingress gateway nftables is present")
postUpCmd, postDownCmd = firewallNFTCommandsCreateIngress(iface)
default:
logger.Log(3, "creating ingress gateway using nftables is not present")
postUpCmd, postDownCmd = firewallIPTablesCommandsCreateIngress(iface, ipv4, ipv6)
}
if node.PostUp != "" {
if !strings.Contains(node.PostUp, postUpCmd) {
postUpCmd = node.PostUp + postUpCmd
}
}
if node.PostDown != "" {
if !strings.Contains(node.PostDown, postDownCmd) {
postDownCmd = node.PostDown + postDownCmd
}
}
node.SetLastModified()
node.PostUp = postUpCmd
node.PostDown = postDownCmd
if failover && servercfg.Is_EE {
node.Failover = true
}
@@ -274,10 +160,6 @@ func DeleteIngressGateway(networkName string, nodeid string) (models.Node, bool,
node.IngressGatewayRange = ""
node.Failover = false
// default to removing postup and postdown
node.PostUp = ""
node.PostDown = ""
//logger.Log(3, "deleting ingress gateway firewall in use is '", host.FirewallInUse, "' and isEgressGateway is", node.IsEgressGateway)
if node.EgressGatewayRequest.NodeID != "" {
_, err := CreateEgressGateway(node.EgressGatewayRequest)
@@ -315,128 +197,3 @@ func DeleteGatewayExtClients(gatewayID string, networkName string) error {
}
return nil
}
// firewallNFTCommandsCreateIngress - used to centralize firewall command maintenance for creating an ingress gateway using the nftables firewall.
func firewallNFTCommandsCreateIngress(networkInterface string) (string, string) {
// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
postUp := "nft add table ip filter ; "
postUp += "nft add chain ip filter FORWARD ; "
postUp += "nft add rule ip filter FORWARD iifname " + networkInterface + " counter accept ; "
postUp += "nft add rule ip filter FORWARD oifname " + networkInterface + " counter accept ; "
postUp += "nft add table nat ; "
postUp += "nft add chain nat postrouting ; "
postUp += "nft add rule ip nat postrouting oifname " + networkInterface + " counter masquerade ; "
// doesn't remove potentially empty tables or chains
postDown := "nft flush table filter ; "
postDown += "nft flush table nat ; "
return postUp, postDown
}
// firewallNFTCommandsCreateEgress - used to centralize firewall command maintenance for creating an egress gateway using the nftables firewall.
func firewallNFTCommandsCreateEgress(networkInterface string, gatewayInterface string, gatewayranges []string, egressNatEnabled bool, ipv4, ipv6 bool) (string, string) {
// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
postUp := ""
postDown := ""
if ipv4 {
postUp += "nft add table ip filter ; "
postUp += "nft add chain ip filter forward ; "
postUp += "nft add rule filter forward ct state related,established accept ; "
postUp += "nft add rule ip filter forward iifname " + networkInterface + " accept ; "
postUp += "nft add rule ip filter forward oifname " + networkInterface + " accept ; "
postUp += "nft add table nat ; "
postUp += "nft 'add chain ip nat prerouting { type nat hook prerouting priority 0 ;}' ; "
postUp += "nft 'add chain ip nat postrouting { type nat hook postrouting priority 0 ;}' ; "
postDown += "nft flush table filter ; "
if egressNatEnabled {
postUp += "nft add table nat ; "
postUp += "nft add chain nat postrouting ; "
postUp += "nft add rule ip nat postrouting oifname " + gatewayInterface + " counter masquerade ; "
postDown += "nft flush table nat ; "
}
}
if ipv6 {
postUp += "nft add table ip6 filter ; "
postUp += "nft add chain ip6 filter forward ; "
postUp += "nft add rule ip6 filter forward ct state related,established accept ; "
postUp += "nft add rule ip6 filter forward iifname " + networkInterface + " accept ; "
postUp += "nft add rule ip6 filter forward oifname " + networkInterface + " accept ; "
postDown += "nft flush table ip6 filter ; "
if egressNatEnabled {
postUp += "nft add table ip6 nat ; "
postUp += "nft 'add chain ip6 nat prerouting { type nat hook prerouting priority 0 ;}' ; "
postUp += "nft 'add chain ip6 nat postrouting { type nat hook postrouting priority 0 ;}' ; "
postUp += "nft add rule ip6 nat postrouting oifname " + gatewayInterface + " masquerade ; "
postDown += "nft flush table ip6 nat ; "
}
}
return postUp, postDown
}
// firewallIPTablesCommandsCreateIngress - used to centralize firewall command maintenance for creating an ingress gateway using the iptables firewall.
func firewallIPTablesCommandsCreateIngress(networkInterface string, ipv4, ipv6 bool) (string, string) {
postUp := ""
postDown := ""
if ipv4 {
// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
postUp += "iptables -A FORWARD -i " + networkInterface + " -j ACCEPT ; "
postUp += "iptables -A FORWARD -o " + networkInterface + " -j ACCEPT ; "
postUp += "iptables -t nat -A POSTROUTING -o " + networkInterface + " -j MASQUERADE ; "
// doesn't remove potentially empty tables or chains
postDown += "iptables -D FORWARD -i " + networkInterface + " -j ACCEPT ; "
postDown += "iptables -D FORWARD -o " + networkInterface + " -j ACCEPT ; "
postDown += "iptables -t nat -D POSTROUTING -o " + networkInterface + " -j MASQUERADE ; "
}
if ipv6 {
// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
postUp += "ip6tables -A FORWARD -i " + networkInterface + " -j ACCEPT ; "
postUp += "ip6tables -A FORWARD -o " + networkInterface + " -j ACCEPT ; "
postUp += "ip6tables -t nat -A POSTROUTING -o " + networkInterface + " -j MASQUERADE ; "
// doesn't remove potentially empty tables or chains
postDown += "ip6tables -D FORWARD -i " + networkInterface + " -j ACCEPT ; "
postDown += "ip6tables -D FORWARD -o " + networkInterface + " -j ACCEPT ; "
postDown += "ip6tables -t nat -D POSTROUTING -o " + networkInterface + " -j MASQUERADE ; "
}
return postUp, postDown
}
// firewallIPTablesCommandsCreateEgress - used to centralize firewall command maintenance for creating an egress gateway using the iptables firewall.
func firewallIPTablesCommandsCreateEgress(networkInterface string, gatewayInterface string, egressNatEnabled bool, ipv4, ipv6 bool) (string, string) {
// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
postUp := ""
postDown := ""
if ipv4 {
postUp += "iptables -A FORWARD -i " + networkInterface + " -j ACCEPT ; "
postUp += "iptables -A FORWARD -o " + networkInterface + " -j ACCEPT ; "
postDown += "iptables -D FORWARD -i " + networkInterface + " -j ACCEPT ; "
postDown += "iptables -D FORWARD -o " + networkInterface + " -j ACCEPT ; "
if egressNatEnabled {
postUp += "iptables -t nat -A POSTROUTING -o " + gatewayInterface + " -j MASQUERADE ; "
postDown += "iptables -t nat -D POSTROUTING -o " + gatewayInterface + " -j MASQUERADE ; "
}
}
if ipv6 {
postUp += "ip6tables -A FORWARD -i " + networkInterface + " -j ACCEPT ; "
postUp += "ip6tables -A FORWARD -o " + networkInterface + " -j ACCEPT ; "
postDown += "ip6tables -D FORWARD -i " + networkInterface + " -j ACCEPT ; "
postDown += "ip6tables -D FORWARD -o " + networkInterface + " -j ACCEPT ; "
if egressNatEnabled {
postUp += "ip6tables -t nat -A POSTROUTING -o " + gatewayInterface + " -j MASQUERADE ; "
postDown += "ip6tables -t nat -D POSTROUTING -o " + gatewayInterface + " -j MASQUERADE ; "
}
}
return postUp, postDown
}

62
logic/host_test.go Normal file
View File

@@ -0,0 +1,62 @@
package logic
import (
"net"
"testing"
"github.com/google/uuid"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/models"
"github.com/matryer/is"
)
func TestCheckPorts(t *testing.T) {
database.InitializeDatabase()
h := models.Host{
ID: uuid.New(),
EndpointIP: net.ParseIP("192.168.1.1"),
ListenPort: 51821,
ProxyListenPort: maxPort,
}
testHost := models.Host{
ID: uuid.New(),
EndpointIP: net.ParseIP("192.168.1.1"),
ListenPort: 51830,
ProxyListenPort: 51730,
}
CreateHost(&h)
t.Run("no change", func(t *testing.T) {
is := is.New(t)
CheckHostPorts(&testHost)
is.Equal(testHost.ListenPort, 51830)
is.Equal(testHost.ProxyListenPort, 51730)
})
t.Run("same listen port", func(t *testing.T) {
is := is.New(t)
testHost.ListenPort = 51821
CheckHostPorts(&testHost)
is.Equal(testHost.ListenPort, 51822)
is.Equal(testHost.ProxyListenPort, 51730)
})
t.Run("same proxy port", func(t *testing.T) {
is := is.New(t)
testHost.ProxyListenPort = 65535
CheckHostPorts(&testHost)
is.Equal(testHost.ListenPort, 51822)
is.Equal(testHost.ProxyListenPort, minPort)
})
t.Run("listenport equals proxy port", func(t *testing.T) {
is := is.New(t)
testHost.ListenPort = maxPort
CheckHostPorts(&testHost)
is.Equal(testHost.ListenPort, minPort)
is.Equal(testHost.ProxyListenPort, minPort+1)
})
t.Run("proxyport equals listenport", func(t *testing.T) {
is := is.New(t)
testHost.ProxyListenPort = 51821
CheckHostPorts(&testHost)
is.Equal(testHost.ListenPort, minPort)
is.Equal(testHost.ProxyListenPort, 51822)
})
}

View File

@@ -20,6 +20,11 @@ var (
ErrInvalidHostID error = errors.New("invalid host id")
)
const (
maxPort = 1<<16 - 1
minPort = 1025
)
// GetAllHosts - returns all hosts in flat list or error
func GetAllHosts() ([]models.Host, error) {
currHostMap, err := GetHostsMap()
@@ -117,10 +122,6 @@ func UpdateHost(newHost, currentHost *models.Host) {
newHost.Name = currentHost.Name
}
if newHost.LocalRange.String() != currentHost.LocalRange.String() {
newHost.LocalRange = currentHost.LocalRange
}
if newHost.MTU == 0 {
newHost.MTU = currentHost.MTU
}
@@ -132,6 +133,42 @@ func UpdateHost(newHost, currentHost *models.Host) {
if newHost.ProxyListenPort == 0 {
newHost.ProxyListenPort = currentHost.ProxyListenPort
}
newHost.PublicListenPort = currentHost.PublicListenPort
}
// UpdateHostFromClient - used for updating host on server with update recieved from client
func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool) {
if newHost.ListenPort != 0 && currHost.ListenPort != newHost.ListenPort {
currHost.ListenPort = newHost.ListenPort
sendPeerUpdate = true
}
if newHost.ProxyListenPort != 0 && currHost.ProxyListenPort != newHost.ProxyListenPort {
currHost.ProxyListenPort = newHost.ProxyListenPort
sendPeerUpdate = true
}
if newHost.PublicListenPort != 0 && currHost.PublicListenPort != newHost.PublicListenPort {
currHost.PublicListenPort = newHost.PublicListenPort
sendPeerUpdate = true
}
if currHost.ProxyEnabled != newHost.ProxyEnabled {
currHost.ProxyEnabled = newHost.ProxyEnabled
sendPeerUpdate = true
}
if currHost.EndpointIP.String() != newHost.EndpointIP.String() {
currHost.EndpointIP = newHost.EndpointIP
sendPeerUpdate = true
}
currHost.DaemonInstalled = newHost.DaemonInstalled
currHost.Debug = newHost.Debug
currHost.Verbosity = newHost.Verbosity
currHost.Version = newHost.Version
if newHost.Name != "" {
currHost.Name = newHost.Name
}
return
}
// UpsertHost - upserts into DB a given host model, does not check for existence*
@@ -328,3 +365,47 @@ func GetRelatedHosts(hostID string) []models.Host {
}
return relatedHosts
}
// CheckHostPort checks host endpoints to ensures that hosts on the same server
// with the same endpoint have different listen ports
// in the case of 64535 hosts or more with same endpoint, ports will not be changed
func CheckHostPorts(h *models.Host) {
portsInUse := make(map[int]bool)
hosts, err := GetAllHosts()
if err != nil {
return
}
for _, host := range hosts {
if host.ID == h.ID {
//skip self
continue
}
if !host.EndpointIP.Equal(h.EndpointIP) {
continue
}
portsInUse[host.ListenPort] = true
portsInUse[host.ProxyListenPort] = true
}
// iterate until port is not found or max iteration is reached
for i := 0; portsInUse[h.ListenPort] && i < maxPort-minPort+1; i++ {
updatePort(&h.ListenPort)
}
// allocate h.ListenPort so it is unavailable to h.ProxyListenPort
portsInUse[h.ListenPort] = true
for i := 0; portsInUse[h.ProxyListenPort] && i < maxPort-minPort+1; i++ {
updatePort(&h.ProxyListenPort)
}
}
// HostExists - checks if given host already exists
func HostExists(h *models.Host) bool {
_, err := GetHost(h.ID.String())
return (err != nil && !database.IsEmptyRecord(err)) || (err == nil)
}
func updatePort(p *int) {
*p++
if *p > maxPort {
*p = minPort
}
}

View File

@@ -130,7 +130,7 @@ func VerifyUserToken(tokenString string) (username string, networks []string, is
}
// VerifyToken - [nodes] Only
func VerifyToken(tokenString string) (nodeID string, mac string, network string, err error) {
func VerifyToken(tokenString string) (hostID string, mac string, network string, err error) {
claims := &models.Claims{}
// this may be a stupid way of serving up a master key

View File

@@ -3,9 +3,9 @@ package metrics
import (
"time"
proxy_metrics "github.com/gravitl/netclient/nmproxy/metrics"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
proxy_metrics "github.com/gravitl/netmaker/metrics"
"github.com/gravitl/netmaker/models"
"golang.zx2c4.com/wireguard/wgctrl"
)

View File

@@ -237,12 +237,12 @@ func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
continue
}
if isIpv6 {
if (extClient.Address6 == ip || extClient.InternalIPAddr6 == ip) && extClient.Network == network {
if (extClient.Address6 == ip) && extClient.Network == network {
return false
}
} else {
if (extClient.Address == ip || extClient.InternalIPAddr == ip) && extClient.Network == network {
if (extClient.Address == ip) && extClient.Network == network {
return false
}
}
@@ -298,60 +298,6 @@ func UniqueAddress6(networkName string, reverse bool) (net.IP, error) {
return add, errors.New("ERROR: No unique IPv6 addresses available. Check network subnet")
}
// GetLocalIP - gets the local ip
func GetLocalIP(node models.Node) string {
var local string
ifaces, err := net.Interfaces()
if err != nil {
return local
}
host, err := GetHost(node.HostID.String())
if err != nil {
return local
}
localrange := host.LocalRange
found := false
for _, i := range ifaces {
if i.Flags&net.FlagUp == 0 {
continue // interface down
}
if i.Flags&net.FlagLoopback != 0 {
continue // loopback interface
}
addrs, err := i.Addrs()
if err != nil {
return local
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
if !found {
ip = v.IP
local = ip.String()
if node.IsLocal {
found = localrange.Contains(ip)
} else {
found = true
}
}
case *net.IPAddr:
if !found {
ip = v.IP
local = ip.String()
if node.IsLocal {
found = localrange.Contains(ip)
} else {
found = true
}
}
}
}
}
return local
}
// UpdateNetworkLocalAddresses - updates network localaddresses
func UpdateNetworkLocalAddresses(networkName string) error {
@@ -517,14 +463,13 @@ func IsNetworkNameUnique(network *models.Network) (bool, error) {
}
// UpdateNetwork - updates a network with another network's fields
func UpdateNetwork(currentNetwork *models.Network, newNetwork *models.Network) (bool, bool, bool, bool, []string, []string, error) {
func UpdateNetwork(currentNetwork *models.Network, newNetwork *models.Network) (bool, bool, bool, []string, []string, error) {
if err := ValidateNetwork(newNetwork, true); err != nil {
return false, false, false, false, nil, nil, err
return false, false, false, nil, nil, err
}
if newNetwork.NetID == currentNetwork.NetID {
hasrangeupdate4 := newNetwork.AddressRange != currentNetwork.AddressRange
hasrangeupdate6 := newNetwork.AddressRange6 != currentNetwork.AddressRange6
localrangeupdate := newNetwork.LocalRange != currentNetwork.LocalRange
hasholepunchupdate := newNetwork.DefaultUDPHolePunch != currentNetwork.DefaultUDPHolePunch
groupDelta := append(StringDifference(newNetwork.ProSettings.AllowedGroups, currentNetwork.ProSettings.AllowedGroups),
StringDifference(currentNetwork.ProSettings.AllowedGroups, newNetwork.ProSettings.AllowedGroups)...)
@@ -532,14 +477,14 @@ func UpdateNetwork(currentNetwork *models.Network, newNetwork *models.Network) (
StringDifference(currentNetwork.ProSettings.AllowedUsers, newNetwork.ProSettings.AllowedUsers)...)
data, err := json.Marshal(newNetwork)
if err != nil {
return false, false, false, false, nil, nil, err
return false, false, false, nil, nil, err
}
newNetwork.SetNetworkLastModified()
err = database.Insert(newNetwork.NetID, string(data), database.NETWORKS_TABLE_NAME)
return hasrangeupdate4, hasrangeupdate6, localrangeupdate, hasholepunchupdate, groupDelta, userDelta, err
return hasrangeupdate4, hasrangeupdate6, hasholepunchupdate, groupDelta, userDelta, err
}
// copy values
return false, false, false, false, nil, nil, errors.New("failed to update network " + newNetwork.NetID + ", cannot change netid.")
return false, false, false, nil, nil, errors.New("failed to update network " + newNetwork.NetID + ", cannot change netid.")
}
// GetNetwork - gets a network from database

View File

@@ -50,7 +50,7 @@ func GetNetworkNodes(network string) ([]models.Node, error) {
func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
if newNode.Address.IP.String() != currentNode.Address.IP.String() {
if network, err := GetParentNetwork(newNode.Network); err == nil {
if !IsAddressInCIDR(newNode.Address.IP.String(), network.AddressRange) {
if !IsAddressInCIDR(newNode.Address.IP, network.AddressRange) {
return fmt.Errorf("invalid address provided; out of network range for node %s", newNode.ID)
}
}
@@ -203,7 +203,7 @@ func GetAllNodes() ([]models.Node, error) {
var node models.Node
// ignore legacy nodes in database
if err := json.Unmarshal([]byte(value), &node); err != nil {
logger.Log(1, "legacy node detected: ", err.Error())
logger.Log(3, "legacy node detected: ", err.Error())
continue
}
// add node to our array
@@ -239,7 +239,6 @@ func SetNodeDefaults(node *models.Node) {
if err == nil {
node.NetworkRange6 = *cidr
}
node.ExpirationDateTime = time.Now().Add(models.TEN_YEARS_IN_SECONDS)
if node.DefaultACL == "" {
node.DefaultACL = parentNetwork.DefaultACL
@@ -248,49 +247,10 @@ func SetNodeDefaults(node *models.Node) {
if node.PersistentKeepalive == 0 {
node.PersistentKeepalive = time.Second * time.Duration(parentNetwork.DefaultKeepalive)
}
if node.PostUp == "" {
postup := parentNetwork.DefaultPostUp
node.PostUp = postup
}
if node.PostDown == "" {
postdown := parentNetwork.DefaultPostDown
node.PostDown = postdown
}
// == Parent Network settings ==
// == node defaults if not set by parent ==
///TODO ___ REVISIT ------
///TODO ___ REVISIT ------
///TODO ___ REVISIT ------
///TODO ___ REVISIT ------
///TODO ___ REVISIT ------
//node.SetIPForwardingDefault()
//node.SetDNSOnDefault()
//node.SetIsLocalDefault()
//node.SetLastModified()
//node.SetDefaultName()
//node.SetLastCheckIn()
//node.SetLastPeerUpdate()
//node.SetDefaultAction()
//node.SetIsServerDefault()
//node.SetIsStaticDefault()
//node.SetDefaultEgressGateway()
//node.SetDefaultIngressGateway()
//node.SetDefaulIsPending()
//node.SetDefaultMTU()
//node.SetDefaultNFTablesPresent()
//node.SetDefaultIsRelayed()
//node.SetDefaultIsRelay()
//node.SetDefaultIsDocker()
//node.SetDefaultIsK8S()
//node.SetDefaultIsHub()
//node.SetDefaultConnected()
//node.SetDefaultACL()
//node.SetDefaultFailover()
///TODO ___ REVISIT ------
///TODO ___ REVISIT ------
///TODO ___ REVISIT ------
///TODO ___ REVISIT ------
node.SetLastModified()
node.SetLastCheckIn()
node.SetDefaultConnected()
node.SetExpirationDateTime()
}
// GetRecordKey - get record key
@@ -302,30 +262,6 @@ func GetRecordKey(id string, network string) (string, error) {
return id + "###" + network, nil
}
// GetNodeByMacAddress - gets a node by mac address
func GetNodeByMacAddress(network string, macaddress string) (models.Node, error) {
var node models.Node
key, err := GetRecordKey(macaddress, network)
if err != nil {
return node, err
}
record, err := database.FetchRecord(database.NODES_TABLE_NAME, key)
if err != nil {
return models.Node{}, err
}
if err = json.Unmarshal([]byte(record), &node); err != nil {
return models.Node{}, err
}
SetNodeDefaults(&node)
return node, nil
}
// GetNodesByAddress - gets a node by mac address
func GetNodesByAddress(network string, addresses []string) ([]models.Node, error) {
var nodes []models.Node

View File

@@ -5,12 +5,12 @@ import (
"fmt"
"log"
"net"
"net/netip"
"sort"
"strconv"
"strings"
"time"
proxy_models "github.com/gravitl/netclient/nmproxy/models"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic/acls/nodeacls"
@@ -28,10 +28,10 @@ import (
// TODO ==========================
// TODO ==========================
// revisit this logic with new host/node models.
func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyManagerPayload, error) {
proxyPayload := proxy_models.ProxyManagerPayload{}
func GetPeersForProxy(node *models.Node, onlyPeers bool) (models.ProxyManagerPayload, error) {
proxyPayload := models.ProxyManagerPayload{}
var peers []wgtypes.PeerConfig
peerConfMap := make(map[string]proxy_models.PeerConf)
peerConfMap := make(map[string]models.PeerConf)
var err error
currentPeers, err := GetNetworkNodes(node.Network)
if err != nil {
@@ -70,8 +70,9 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
logger.Log(1, "failed to relayed nodes: ", node.ID.String(), err.Error())
proxyPayload.IsRelay = false
} else {
relayPeersMap := make(map[string]proxy_models.RelayedConf)
relayPeersMap := make(map[string]models.RelayedConf)
for _, relayedNode := range relayedNodes {
relayedNode := relayedNode
payload, err := GetPeersForProxy(&relayedNode, true)
if err == nil {
relayedHost, err := GetHost(relayedNode.HostID.String())
@@ -80,7 +81,7 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
}
relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedHost.EndpointIP, host.ListenPort))
if udpErr == nil {
relayPeersMap[host.PublicKey.String()] = proxy_models.RelayedConf{
relayPeersMap[host.PublicKey.String()] = models.RelayedConf{
RelayedPeerEndpoint: relayedEndpoint,
RelayedPeerPubKey: relayedHost.PublicKey.String(),
Peers: payload.Peers,
@@ -110,7 +111,7 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
if proxyStatus {
listenPort = host.ProxyListenPort
if listenPort == 0 {
listenPort = proxy_models.NmProxyPort
listenPort = models.NmProxyPort
}
} else if listenPort == 0 {
listenPort = host.ListenPort
@@ -135,7 +136,7 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
PersistentKeepaliveInterval: &keepalive,
ReplaceAllowedIPs: true,
})
peerConfMap[host.PublicKey.String()] = proxy_models.PeerConf{
peerConfMap[host.PublicKey.String()] = models.PeerConf{
Address: net.ParseIP(peer.PrimaryAddress()),
Proxy: proxyStatus,
PublicListenPort: int32(listenPort),
@@ -151,7 +152,7 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
}
relayTo, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayHost.EndpointIP, relayHost.ListenPort))
if err == nil {
peerConfMap[host.PublicKey.String()] = proxy_models.PeerConf{
peerConfMap[host.PublicKey.String()] = models.PeerConf{
IsRelayed: true,
RelayedTo: relayTo,
@@ -192,11 +193,11 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
}
// GetProxyUpdateForHost - gets the proxy update for host
func GetProxyUpdateForHost(host *models.Host) (proxy_models.ProxyManagerPayload, error) {
proxyPayload := proxy_models.ProxyManagerPayload{
Action: proxy_models.ProxyUpdate,
func GetProxyUpdateForHost(host *models.Host) (models.ProxyManagerPayload, error) {
proxyPayload := models.ProxyManagerPayload{
Action: models.ProxyUpdate,
}
peerConfMap := make(map[string]proxy_models.PeerConf)
peerConfMap := make(map[string]models.PeerConf)
if host.IsRelayed {
relayHost, err := GetHost(host.RelayedBy)
if err == nil {
@@ -213,13 +214,14 @@ func GetProxyUpdateForHost(host *models.Host) (proxy_models.ProxyManagerPayload,
}
if host.IsRelay {
relayedHosts := GetRelayedHosts(host)
relayPeersMap := make(map[string]proxy_models.RelayedConf)
relayPeersMap := make(map[string]models.RelayedConf)
for _, relayedHost := range relayedHosts {
relayedHost := relayedHost
payload, err := GetPeerUpdateForHost(&relayedHost)
if err == nil {
relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedHost.EndpointIP, getPeerListenPort(&relayedHost)))
if udpErr == nil {
relayPeersMap[relayedHost.PublicKey.String()] = proxy_models.RelayedConf{
relayPeersMap[relayedHost.PublicKey.String()] = models.RelayedConf{
RelayedPeerEndpoint: relayedEndpoint,
RelayedPeerPubKey: relayedHost.PublicKey.String(),
Peers: payload.Peers,
@@ -252,11 +254,10 @@ func GetProxyUpdateForHost(host *models.Host) (proxy_models.ProxyManagerPayload,
if err != nil {
continue
}
var currPeerConf proxy_models.PeerConf
var currPeerConf models.PeerConf
var found bool
if currPeerConf, found = peerConfMap[peerHost.PublicKey.String()]; !found {
currPeerConf = proxy_models.PeerConf{
currPeerConf = models.PeerConf{
Proxy: peerHost.ProxyEnabled,
PublicListenPort: int32(getPeerListenPort(peerHost)),
}
@@ -299,12 +300,16 @@ func GetPeerUpdateForHost(host *models.Host) (models.HostPeerUpdate, error) {
}
hostPeerUpdate := models.HostPeerUpdate{
Host: *host,
Server: servercfg.GetServer(),
Network: make(map[string]models.NetworkInfo),
PeerIDs: make(models.HostPeerMap),
ServerVersion: servercfg.GetVersion(),
ServerAddrs: []models.ServerAddr{},
IngressInfo: models.IngressInfo{
ExtPeers: make(map[string]models.ExtClientInfo),
},
}
log.Println("peer update for host ", host.ID.String())
logger.Log(1, "peer update for host ", host.ID.String())
peerIndexMap := make(map[string]int)
for _, nodeID := range host.Nodes {
node, err := GetNodeByID(nodeID)
@@ -314,6 +319,7 @@ func GetPeerUpdateForHost(host *models.Host) (models.HostPeerUpdate, error) {
if !node.Connected || node.Action == models.NODE_DELETE || node.PendingDelete {
continue
}
hostPeerUpdate.Network[node.Network] = models.NetworkInfo{
DNS: getPeerDNS(node.Network),
}
@@ -322,9 +328,13 @@ func GetPeerUpdateForHost(host *models.Host) (models.HostPeerUpdate, error) {
log.Println("no network nodes")
return models.HostPeerUpdate{}, err
}
var extClientPeerMap map[string]models.PeerExtInfo
if node.IsIngressGateway {
extClientPeerMap = make(map[string]models.PeerExtInfo)
}
for _, peer := range currentPeers {
if peer.ID == node.ID {
log.Println("peer update, skipping self")
logger.Log(2, "peer update, skipping self")
//skip yourself
continue
@@ -332,12 +342,12 @@ func GetPeerUpdateForHost(host *models.Host) (models.HostPeerUpdate, error) {
var peerConfig wgtypes.PeerConfig
peerHost, err := GetHost(peer.HostID.String())
if err != nil {
log.Println("no peer host", err)
logger.Log(1, "no peer host", peer.HostID.String(), err.Error())
return models.HostPeerUpdate{}, err
}
if !peer.Connected {
log.Println("peer update, skipping unconnected node")
if !peer.Connected || peer.Action == models.NODE_DELETE || peer.PendingDelete {
logger.Log(2, "peer update, skipping unconnected node", peer.ID.String())
//skip unconnected nodes
continue
}
@@ -383,6 +393,17 @@ func GetPeerUpdateForHost(host *models.Host) (models.HostPeerUpdate, error) {
allowedips = append(allowedips, getEgressIPs(&node, &peer)...)
}
peerConfig.AllowedIPs = allowedips
if node.IsIngressGateway {
extClientPeerMap[peerHost.PublicKey.String()] = models.PeerExtInfo{
PeerAddr: net.IPNet{
IP: net.ParseIP(peer.PrimaryAddress()),
Mask: getCIDRMaskFromAddr(peer.PrimaryAddress()),
},
PeerKey: peerHost.PublicKey.String(),
Allow: true,
}
}
if _, ok := hostPeerUpdate.PeerIDs[peerHost.PublicKey.String()]; !ok {
hostPeerUpdate.PeerIDs[peerHost.PublicKey.String()] = make(map[string]models.IDandAddr)
@@ -408,7 +429,7 @@ func GetPeerUpdateForHost(host *models.Host) (models.HostPeerUpdate, error) {
}
if node.IsIngressGateway {
extPeers, extPeerIDAndAddrs, err := getExtPeers(&node, true)
extPeers, extPeerIDAndAddrs, err := getExtPeers(&node)
if err == nil {
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
@@ -419,6 +440,19 @@ func GetPeerUpdateForHost(host *models.Host) (models.HostPeerUpdate, error) {
Name: extPeerIdAndAddr.Name,
Network: node.Network,
}
hostPeerUpdate.IngressInfo.ExtPeers[extPeerIdAndAddr.ID] = models.ExtClientInfo{
Masquerade: true,
IngGwAddr: net.IPNet{
IP: net.ParseIP(node.PrimaryAddress()),
Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
},
ExtPeerAddr: net.IPNet{
IP: net.ParseIP(extPeerIdAndAddr.Address),
Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
},
ExtPeerKey: extPeerIdAndAddr.ID,
Peers: extClientPeerMap,
}
}
} else if !database.IsEmptyRecord(err) {
@@ -530,21 +564,21 @@ func GetPeerUpdate(node *models.Node, host *models.Host) (models.PeerUpdate, err
return peerUpdate, nil
}
func getRelayAllowedIPs(node, peer *models.Node) []net.IPNet {
var allowedips []net.IPNet
var allowedip net.IPNet
for _, addr := range peer.RelayAddrs {
if node.Address.IP.String() == addr {
continue
}
if node.Address6.IP.String() == addr {
continue
}
allowedip.IP = net.ParseIP(addr)
allowedips = append(allowedips, allowedip)
}
return allowedips
}
// func getRelayAllowedIPs(node, peer *models.Node) []net.IPNet {
// var allowedips []net.IPNet
// var allowedip net.IPNet
// for _, addr := range peer.RelayAddrs {
// if node.Address.IP.String() == addr {
// continue
// }
// if node.Address6.IP.String() == addr {
// continue
// }
// allowedip.IP = net.ParseIP(addr)
// allowedips = append(allowedips, allowedip)
// }
// return allowedips
// }
// GetPeerUpdateLegacy - gets a wireguard peer config for each peer of a node
func GetPeerUpdateLegacy(node *models.Node) (models.PeerUpdate, error) {
@@ -699,7 +733,7 @@ func GetPeerUpdateLegacy(node *models.Node) (models.PeerUpdate, error) {
}
if node.IsIngressGateway {
extPeers, idsAndAddr, err := getExtPeers(node, true)
extPeers, idsAndAddr, err := getExtPeers(node)
if err == nil {
peers = append(peers, extPeers...)
for i := range idsAndAddr {
@@ -722,7 +756,7 @@ func GetPeerUpdateLegacy(node *models.Node) (models.PeerUpdate, error) {
return peerUpdate, nil
}
func getExtPeers(node *models.Node, forIngressNode bool) ([]wgtypes.PeerConfig, []models.IDandAddr, error) {
func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, error) {
var peers []wgtypes.PeerConfig
var idsAndAddr []models.IDandAddr
extPeers, err := GetNetworkExtClients(node.Network)
@@ -740,13 +774,14 @@ func getExtPeers(node *models.Node, forIngressNode bool) ([]wgtypes.PeerConfig,
continue
}
if host.PublicKey.String() == extPeer.PublicKey {
if host.PublicKey.String() == extPeer.PublicKey ||
extPeer.IngressGatewayID != node.ID.String() || !extPeer.Enabled {
continue
}
var allowedips []net.IPNet
var peer wgtypes.PeerConfig
if forIngressNode && extPeer.Address != "" {
if extPeer.Address != "" {
var peeraddr = net.IPNet{
IP: net.ParseIP(extPeer.Address),
Mask: net.CIDRMask(32, 32),
@@ -756,7 +791,7 @@ func getExtPeers(node *models.Node, forIngressNode bool) ([]wgtypes.PeerConfig,
}
}
if forIngressNode && extPeer.Address6 != "" {
if extPeer.Address6 != "" {
var addr6 = net.IPNet{
IP: net.ParseIP(extPeer.Address6),
Mask: net.CIDRMask(128, 128),
@@ -765,26 +800,6 @@ func getExtPeers(node *models.Node, forIngressNode bool) ([]wgtypes.PeerConfig,
allowedips = append(allowedips, addr6)
}
}
if !forIngressNode {
if extPeer.InternalIPAddr != "" {
peerInternalAddr := net.IPNet{
IP: net.ParseIP(extPeer.InternalIPAddr),
Mask: net.CIDRMask(32, 32),
}
if peerInternalAddr.IP != nil && peerInternalAddr.Mask != nil {
allowedips = append(allowedips, peerInternalAddr)
}
}
if extPeer.InternalIPAddr6 != "" {
peerInternalAddr6 := net.IPNet{
IP: net.ParseIP(extPeer.InternalIPAddr6),
Mask: net.CIDRMask(32, 32),
}
if peerInternalAddr6.IP != nil && peerInternalAddr6.Mask != nil {
allowedips = append(allowedips, peerInternalAddr6)
}
}
}
primaryAddr := extPeer.Address
if primaryAddr == "" {
@@ -806,7 +821,7 @@ func getExtPeers(node *models.Node, forIngressNode bool) ([]wgtypes.PeerConfig,
}
func getExtPeersForProxy(node *models.Node, proxyPeerConf map[string]proxy_models.PeerConf) ([]wgtypes.PeerConfig, map[string]proxy_models.PeerConf, error) {
func getExtPeersForProxy(node *models.Node, proxyPeerConf map[string]models.PeerConf) ([]wgtypes.PeerConfig, map[string]models.PeerConf, error) {
var peers []wgtypes.PeerConfig
host, err := GetHost(node.HostID.String())
if err != nil {
@@ -824,7 +839,8 @@ func getExtPeersForProxy(node *models.Node, proxyPeerConf map[string]proxy_model
continue
}
if host.PublicKey.String() == extPeer.PublicKey {
if host.PublicKey.String() == extPeer.PublicKey ||
extPeer.IngressGatewayID != node.ID.String() || !extPeer.Enabled {
continue
}
@@ -855,14 +871,9 @@ func getExtPeersForProxy(node *models.Node, proxyPeerConf map[string]proxy_model
ReplaceAllowedIPs: true,
AllowedIPs: allowedips,
}
extInternalPrimaryAddr := extPeer.InternalIPAddr
if extInternalPrimaryAddr == "" {
extInternalPrimaryAddr = extPeer.InternalIPAddr6
}
extConf := proxy_models.PeerConf{
extConf := models.PeerConf{
IsExtClient: true,
Address: net.ParseIP(extPeer.Address),
ExtInternalIp: net.ParseIP(extInternalPrimaryAddr),
}
proxyPeerConf[peer.PublicKey.String()] = extConf
@@ -879,7 +890,7 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
// handle ingress gateway peers
if peer.IsIngressGateway {
extPeers, _, err := getExtPeers(peer, false)
extPeers, _, err := getExtPeers(peer)
if err != nil {
logger.Log(2, "could not retrieve ext peers for ", peer.ID.String(), err.Error())
}
@@ -1057,7 +1068,7 @@ func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string)
peers = append(peers, peerData)
//if ingress add extclients
if node.IsIngressGateway {
extPeers, _, err := getExtPeers(node, true)
extPeers, _, err := getExtPeers(node)
if err == nil {
peers = append(peers, extPeers...)
} else {
@@ -1140,3 +1151,15 @@ func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
}
return allowedips
}
func getCIDRMaskFromAddr(addr string) net.IPMask {
cidr := net.CIDRMask(32, 32)
ipAddr, err := netip.ParseAddr(addr)
if err != nil {
return cidr
}
if ipAddr.Is6() {
cidr = net.CIDRMask(128, 128)
}
return cidr
}

View File

@@ -3,6 +3,7 @@ package pro
import (
"testing"
"github.com/google/uuid"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/models/promodels"
@@ -18,8 +19,13 @@ func TestNetworkUserLogic(t *testing.T) {
NetID: "skynet",
AddressRange: "192.168.0.0/24",
}
nodes := []models.LegacyNode{
models.LegacyNode{ID: "coolnode"},
tmpCNode := models.CommonNode{
ID: uuid.New(),
}
tempNode := models.Node{}
tempNode.CommonNode = tmpCNode
nodes := []models.Node{
tempNode,
}
clients := []models.ExtClient{
@@ -63,10 +69,10 @@ func TestNetworkUserLogic(t *testing.T) {
})
t.Run("Successful net user node isallowed", func(t *testing.T) {
networkUser.Nodes = append(networkUser.Nodes, "coolnode")
networkUser.Nodes = append(networkUser.Nodes, nodes[0].ID.String())
err := UpdateNetworkUser(network.NetID, &networkUser)
assert.Nil(t, err)
isUserNodeAllowed := IsUserNodeAllowed(nodes[:], network.NetID, string(networkUser.ID), "coolnode")
isUserNodeAllowed := IsUserNodeAllowed(nodes[:], network.NetID, string(networkUser.ID), nodes[0].ID.String())
assert.True(t, isUserNodeAllowed)
})

View File

@@ -1,8 +1,6 @@
package logic
import (
"strings"
"github.com/google/uuid"
"github.com/gravitl/netmaker/models"
)
@@ -33,9 +31,3 @@ func EnterpriseCheck() {
check()
}
}
// == Private ==
func isDeleteError(err error) bool {
return err != nil && strings.Contains(err.Error(), models.NODE_DELETE)
}

View File

@@ -19,9 +19,6 @@ var (
Free_Tier = false
)
// constant for database key for storing server ids
const server_id_key = "nm-server-id"
type serverData struct {
PrivateKey string `json:"privatekey,omitempty" bson:"privatekey,omitempty"`
}

View File

@@ -5,7 +5,6 @@ import (
crand "crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"math/big"
"math/rand"
"net"
@@ -15,8 +14,6 @@ import (
"github.com/c-robinson/iplib"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// IsBase64 - checks if a string is in base64 format
@@ -42,26 +39,12 @@ func FileExists(f string) bool {
}
// IsAddressInCIDR - util to see if an address is in a cidr or not
func IsAddressInCIDR(address, cidr string) bool {
func IsAddressInCIDR(address net.IP, cidr string) bool {
var _, currentCIDR, cidrErr = net.ParseCIDR(cidr)
if cidrErr != nil {
return false
}
var addrParts = strings.Split(address, ".")
var addrPartLength = len(addrParts)
if addrPartLength != 4 {
return false
} else {
if addrParts[addrPartLength-1] == "0" ||
addrParts[addrPartLength-1] == "255" {
return false
}
}
ip, _, err := net.ParseCIDR(fmt.Sprintf("%s/32", address))
if err != nil {
return false
}
return currentCIDR.Contains(ip)
return currentCIDR.Contains(address)
}
// SetNetworkNodesLastModified - sets the network nodes last modified
@@ -113,26 +96,6 @@ func RandomString(length int) string {
return string(b)
}
// == Private Methods ==
func setIPForwardingLinux() error {
out, err := ncutils.RunCmd("sysctl net.ipv4.ip_forward", true)
if err != nil {
logger.Log(0, "WARNING: Error encountered setting ip forwarding. This can break functionality.")
return err
} else {
s := strings.Fields(string(out))
if s[2] != "1" {
_, err = ncutils.RunCmd("sysctl -w net.ipv4.ip_forward=1", true)
if err != nil {
logger.Log(0, "WARNING: Error encountered setting ip forwarding. You may want to investigate this.")
return err
}
}
}
return nil
}
// StringSliceContains - sees if a string slice contains a string element
func StringSliceContains(slice []string, item string) bool {
for _, s := range slice {
@@ -143,8 +106,6 @@ func StringSliceContains(slice []string, item string) bool {
return false
}
// == private ==
// NormalCIDR - returns the first address of CIDR
func NormalizeCIDR(address string) (string, error) {
ip, IPNet, err := net.ParseCIDR(address)
@@ -161,23 +122,6 @@ func NormalizeCIDR(address string) (string, error) {
return IPNet.String(), nil
}
func getNetworkProtocols(cidrs []string) (bool, bool) {
ipv4 := false
ipv6 := false
for _, cidr := range cidrs {
ip, _, err := net.ParseCIDR(cidr)
if err != nil {
continue
}
if ip.To4() == nil {
ipv6 = true
} else {
ipv4 = true
}
}
return ipv4, ipv6
}
// StringDifference - returns the elements in `a` that aren't in `b`.
func StringDifference(a, b []string) []string {
mb := make(map[string]struct{}, len(b))
@@ -206,3 +150,5 @@ func CheckIfFileExists(filePath string) bool {
func RemoveStringSlice(slice []string, i int) []string {
return append(slice[:i], slice[i+1:]...)
}
// == private ==

View File

@@ -25,6 +25,7 @@ var (
// CheckZombies - checks if new node has same macaddress as existing node
// if so, existing node is added to zombie node quarantine list
// also cleans up nodes past their expiration date
func CheckZombies(newnode *models.Node, mac net.HardwareAddr) {
nodes, err := GetNetworkNodes(newnode.Network)
if err != nil {
@@ -32,12 +33,11 @@ func CheckZombies(newnode *models.Node, mac net.HardwareAddr) {
return
}
for _, node := range nodes {
host, err := GetHost(node.HostID.String())
if err != nil {
// should we delete the node if host not found ??
if node.ID == newnode.ID {
//skip self
continue
}
if host.MacAddress.String() == mac.String() {
if node.HostID == newnode.HostID || time.Now().After(node.ExpirationDateTime) {
logger.Log(0, "adding ", node.ID.String(), " to zombie list")
newZombie <- node.ID
}
@@ -45,7 +45,7 @@ func CheckZombies(newnode *models.Node, mac net.HardwareAddr) {
}
// ManageZombies - goroutine which adds/removes/deletes nodes from the zombie node quarantine list
func ManageZombies(ctx context.Context) {
func ManageZombies(ctx context.Context, peerUpdate chan *models.Node) {
logger.Log(2, "Zombie management started")
InitializeZombies()
for {
@@ -80,11 +80,13 @@ func ManageZombies(ctx context.Context) {
zombies = append(zombies[:i], zombies[i+1:]...)
continue
}
if time.Since(node.LastCheckIn) > time.Minute*ZOMBIE_DELETE_TIME {
if time.Since(node.LastCheckIn) > time.Minute*ZOMBIE_DELETE_TIME || time.Now().After(node.ExpirationDateTime) {
if err := DeleteNode(&node, true); err != nil {
logger.Log(1, "error deleting zombie node", zombies[i].String(), err.Error())
continue
}
node.Action = models.NODE_DELETE
peerUpdate <- &node
logger.Log(1, "deleting zombie node", node.ID.String())
zombies = append(zombies[:i], zombies[i+1:]...)
}

16
main.go
View File

@@ -110,12 +110,6 @@ func initialize() { // Client Mode Prereq Check
logger.FatalLog("To run in client mode requires root privileges. Either disable client mode or run with sudo.")
}
}
// initialize iptables to ensure gateways work correctly and mq is forwarded if containerized
if servercfg.ManageIPTables() != "off" {
if err = serverctl.InitIPTables(true); err != nil {
logger.FatalLog("Unable to initialize iptables on host:", err.Error())
}
}
if servercfg.IsDNSMode() {
err := functions.SetDNSDir()
@@ -191,7 +185,15 @@ func runMessageQueue(wg *sync.WaitGroup) {
mq.SetupMQTT()
ctx, cancel := context.WithCancel(context.Background())
go mq.Keepalive(ctx)
go logic.ManageZombies(ctx)
go func() {
peerUpdate := make(chan *models.Node)
go logic.ManageZombies(ctx, peerUpdate)
for nodeUpdate := range peerUpdate {
if err := mq.NodeUpdate(nodeUpdate); err != nil {
logger.Log(0, "failed to send peer update for deleted node: ", nodeUpdate.ID.String(), err.Error())
}
}
}()
go logic.PurgePendingNodes(ctx)
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGTERM, os.Interrupt)

111
metrics/metrics.go Normal file
View File

@@ -0,0 +1,111 @@
package metrics
import (
"fmt"
"sync"
"time"
"github.com/go-ping/ping"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
)
// lock for metrics map
var metricsMapLock = &sync.RWMutex{}
// metrics data map
var metricsPeerMap = make(map[string]map[string]*models.ProxyMetric)
// GetMetricByServer - get metric data of peers by server
func GetMetricByServer(server string) map[string]*models.ProxyMetric {
metricsMapLock.RLock()
defer metricsMapLock.RUnlock()
if _, ok := metricsPeerMap[server]; !ok {
return nil
}
return metricsPeerMap[server]
}
// GetMetric - fetches the metric data for the peer
func GetMetric(server, peerKey string) models.ProxyMetric {
metric := models.ProxyMetric{}
peerMetricMap := GetMetricByServer(server)
metricsMapLock.RLock()
defer metricsMapLock.RUnlock()
if peerMetricMap == nil {
return metric
}
if m, ok := peerMetricMap[peerKey]; ok && m != nil {
metric = *m
}
return metric
}
// UpdateMetric - updates metric data for the peer
func UpdateMetric(server, peerKey string, metric *models.ProxyMetric) {
metricsMapLock.Lock()
defer metricsMapLock.Unlock()
if metricsPeerMap[server] == nil {
metricsPeerMap[server] = make(map[string]*models.ProxyMetric)
}
metricsPeerMap[server][peerKey] = metric
}
// UpdateMetricByPeer - updates metrics data by peer public key
func UpdateMetricByPeer(peerKey string, metric *models.ProxyMetric, onlyTraffic bool) {
metricsMapLock.Lock()
defer metricsMapLock.Unlock()
for server, peerKeyMap := range metricsPeerMap {
if peerMetric, ok := peerKeyMap[peerKey]; ok {
peerMetric.TrafficRecieved += metric.TrafficRecieved
peerMetric.TrafficSent += metric.TrafficSent
if !onlyTraffic {
peerMetric.LastRecordedLatency = metric.LastRecordedLatency
}
metricsPeerMap[server][peerKey] = peerMetric
}
}
}
// ResetMetricsForPeer - reset metrics for peer
func ResetMetricsForPeer(server, peerKey string) {
metricsMapLock.Lock()
defer metricsMapLock.Unlock()
delete(metricsPeerMap[server], peerKey)
}
// ResetMetricForNode - resets node level metrics
func ResetMetricForNode(server, peerKey, peerID string) {
metric := GetMetric(server, peerKey)
delete(metric.NodeConnectionStatus, peerID)
UpdateMetric(server, peerKey, &metric)
}
// MetricCollectionInterval - collection interval for metrics
const MetricCollectionInterval = time.Second * 25
// PeerConnectionStatus - get peer connection status by pinging
func PeerConnectionStatus(address string) (connected bool) {
fmt.Println("PINGER ADDR: ", address)
pinger, err := ping.NewPinger(address)
if err != nil {
logger.Log(0, "could not initiliaze ping peer address", address, err.Error())
connected = false
} else {
pinger.Timeout = time.Second * 2
err = pinger.Run()
if err != nil {
logger.Log(0, "failed to ping on peer address", address, err.Error())
return false
} else {
pingStats := pinger.Statistics()
if pingStats.PacketsRecv > 0 {
connected = true
return
}
}
}
return
}

View File

@@ -10,5 +10,4 @@ type AccessToken struct {
type ClientConfig struct {
Network string `json:"network"`
Key string `json:"key"`
LocalRange string `json:"localrange"`
}

View File

@@ -13,9 +13,9 @@ type ApiHost struct {
Debug bool `json:"debug"`
IsStatic bool `json:"isstatic"`
ListenPort int `json:"listenport"`
LocalRange string `json:"localrange"`
LocalListenPort int `json:"locallistenport"`
ProxyListenPort int `json:"proxy_listen_port"`
PublicListenPort int `json:"public_listen_port" yaml:"public_listen_port"`
MTU int `json:"mtu" yaml:"mtu"`
Interfaces []Iface `json:"interfaces" yaml:"interfaces"`
DefaultInterface string `json:"defaultinterface" yaml:"defautlinterface"`
@@ -50,16 +50,13 @@ func (h *Host) ConvertNMHostToAPI() *ApiHost {
}
a.IsStatic = h.IsStatic
a.ListenPort = h.ListenPort
a.LocalRange = h.LocalRange.String()
if isEmptyAddr(a.LocalRange) {
a.LocalRange = ""
}
a.MTU = h.MTU
a.MacAddress = h.MacAddress.String()
a.Name = h.Name
a.OS = h.OS
a.Nodes = h.Nodes
a.ProxyEnabled = h.ProxyEnabled
a.PublicListenPort = h.PublicListenPort
a.ProxyListenPort = h.ProxyListenPort
a.PublicKey = h.PublicKey.String()
a.Verbosity = h.Verbosity
@@ -106,14 +103,6 @@ func (a *ApiHost) ConvertAPIHostToNMHost(currentHost *Host) *Host {
h.RelayedHosts = a.RelayedHosts
h.IsRelay = a.IsRelay
h.IsRelayed = a.IsRelayed
if len(a.LocalRange) > 0 {
_, localRange, err := net.ParseCIDR(a.LocalRange)
if err == nil {
h.LocalRange = *localRange
}
} else if !isEmptyAddr(currentHost.LocalRange.String()) {
h.LocalRange = currentHost.LocalRange
}
h.ProxyEnabled = a.ProxyEnabled
h.IsDefault = a.IsDefault

View File

@@ -14,8 +14,6 @@ type ApiNode struct {
Address string `json:"address" validate:"omitempty,ipv4"`
Address6 string `json:"address6" validate:"omitempty,ipv6"`
LocalAddress string `json:"localaddress" validate:"omitempty,ipv4"`
PostUp string `json:"postup"`
PostDown string `json:"postdown"`
AllowedIPs []string `json:"allowedips"`
PersistentKeepalive int32 `json:"persistentkeepalive"`
LastModified int64 `json:"lastmodified"`
@@ -53,8 +51,6 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
convertedNode.Connected = a.Connected
convertedNode.ID, _ = uuid.Parse(a.ID)
convertedNode.HostID, _ = uuid.Parse(a.HostID)
convertedNode.PostUp = a.PostUp
convertedNode.PostDown = a.PostDown
convertedNode.IsLocal = a.IsLocal
convertedNode.IsRelay = a.IsRelay
convertedNode.IsRelayed = a.IsRelayed
@@ -99,8 +95,8 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
}
ip6, addr6, err := net.ParseCIDR(a.Address6)
if err == nil {
convertedNode.Address = *addr6
convertedNode.Address.IP = ip6
convertedNode.Address6 = *addr6
convertedNode.Address6.IP = ip6
}
convertedNode.FailoverNode, _ = uuid.Parse(a.FailoverNode)
convertedNode.LastModified = time.Unix(a.LastModified, 0)
@@ -127,12 +123,11 @@ func (nm *Node) ConvertToAPINode() *ApiNode {
if isEmptyAddr(apiNode.LocalAddress) {
apiNode.LocalAddress = ""
}
apiNode.PostDown = nm.PostDown
apiNode.PostUp = nm.PostUp
apiNode.PersistentKeepalive = int32(nm.PersistentKeepalive.Seconds())
apiNode.LastModified = nm.LastModified.Unix()
apiNode.LastCheckIn = nm.LastCheckIn.Unix()
apiNode.LastPeerUpdate = nm.LastPeerUpdate.Unix()
apiNode.ExpirationDateTime = nm.ExpirationDateTime.Unix()
apiNode.Network = nm.Network
apiNode.NetworkRange = nm.NetworkRange.String()
if isEmptyAddr(apiNode.NetworkRange) {

View File

@@ -14,6 +14,4 @@ type ExtClient struct {
LastModified int64 `json:"lastmodified" bson:"lastmodified"`
Enabled bool `json:"enabled" bson:"enabled"`
OwnerID string `json:"ownerid" bson:"ownerid"`
InternalIPAddr string `json:"internal_ip_addr" bson:"internal_ip_addr"`
InternalIPAddr6 string `json:"internal_ip_addr6" bson:"internal_ip_addr6"`
}

View File

@@ -24,8 +24,6 @@ type Host struct {
Interface string `json:"interface" yaml:"interface"`
Debug bool `json:"debug" yaml:"debug"`
ListenPort int `json:"listenport" yaml:"listenport"`
LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"`
LocalRange net.IPNet `json:"localrange" yaml:"localrange"`
PublicListenPort int `json:"public_listen_port" yaml:"public_listen_port"`
ProxyListenPort int `json:"proxy_listen_port" yaml:"proxy_listen_port"`
MTU int `json:"mtu" yaml:"mtu"`

8
models/migrate.go Normal file
View File

@@ -0,0 +1,8 @@
package models
// MigrationData struct needed to create new v0.18.0 node from v.0.17.X node
type MigrationData struct {
JoinData JoinData
LegacyNodeID string
Password string
}

View File

@@ -1,7 +1,8 @@
package models
import (
proxy_models "github.com/gravitl/netclient/nmproxy/models"
"net"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
@@ -13,18 +14,41 @@ type PeerUpdate struct {
Peers []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
DNS string `json:"dns" bson:"dns" yaml:"dns"`
PeerIDs PeerMap `json:"peerids" bson:"peerids" yaml:"peerids"`
ProxyUpdate proxy_models.ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
ProxyUpdate ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
}
// HostPeerUpdate - struct for host peer updates
type HostPeerUpdate struct {
Host Host `json:"host" bson:"host" yaml:"host"`
Server string `json:"server" bson:"server" yaml:"server"`
ServerVersion string `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
ServerAddrs []ServerAddr `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
Network map[string]NetworkInfo `json:"network" bson:"network" yaml:"network"`
Peers []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
PeerIDs HostPeerMap `json:"peerids" bson:"peerids" yaml:"peerids"`
ProxyUpdate proxy_models.ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
ProxyUpdate ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
IngressInfo IngressInfo `json:"ingress_info" bson:"ext_peers" yaml:"ext_peers"`
}
// IngressInfo - struct for ingress info
type IngressInfo struct {
ExtPeers map[string]ExtClientInfo `json:"ext_peers" yaml:"ext_peers"`
}
// PeerExtInfo - struct for peer info for an ext. client
type PeerExtInfo struct {
PeerAddr net.IPNet `json:"peer_addr" yaml:"peer_addr"`
PeerKey string `json:"peer_key" yaml:"peer_key"`
Allow bool `json:"allow" yaml:"allow"`
}
// ExtClientInfo - struct for ext. client and it's peers
type ExtClientInfo struct {
IngGwAddr net.IPNet `json:"ingress_gw_addr" yaml:"ingress_gw_addr"`
Masquerade bool `json:"masquerade" yaml:"masquerade"`
ExtPeerAddr net.IPNet `json:"ext_peer_addr" yaml:"ext_peer_addr"`
ExtPeerKey string `json:"ext_peer_key" yaml:"ext_peer_key"`
Peers map[string]PeerExtInfo `json:"peers" yaml:"peers"`
}
// NetworkInfo - struct for network info

View File

@@ -17,7 +17,6 @@ type Network struct {
DefaultInterface string `json:"defaultinterface" bson:"defaultinterface" validate:"min=1,max=15"`
DefaultListenPort int32 `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"`
NodeLimit int32 `json:"nodelimit" bson:"nodelimit"`
DefaultPostUp string `json:"defaultpostup" bson:"defaultpostup"`
DefaultPostDown string `json:"defaultpostdown" bson:"defaultpostdown"`
DefaultKeepalive int32 `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"`
AccessKeys []AccessKey `json:"accesskeys" bson:"accesskeys"`
@@ -25,8 +24,6 @@ type Network struct {
IsLocal string `json:"islocal" bson:"islocal" validate:"checkyesorno"`
IsIPv4 string `json:"isipv4" bson:"isipv4" validate:"checkyesorno"`
IsIPv6 string `json:"isipv6" bson:"isipv6" validate:"checkyesorno"`
IsPointToSite string `json:"ispointtosite" bson:"ispointtosite" validate:"checkyesorno"`
LocalRange string `json:"localrange" bson:"localrange" validate:"omitempty,cidr"`
DefaultUDPHolePunch string `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"`
DefaultExtClientDNS string `json:"defaultextclientdns" bson:"defaultextclientdns"`
DefaultMTU int32 `json:"defaultmtu" bson:"defaultmtu"`
@@ -57,9 +54,6 @@ func (network *Network) SetDefaults() {
if network.IsLocal == "" {
network.IsLocal = "no"
}
if network.IsPointToSite == "" {
network.IsPointToSite = "no"
}
if network.DefaultInterface == "" {
if len(network.NetID) < 13 {
network.DefaultInterface = "nm-" + network.NetID

View File

@@ -13,13 +13,4 @@ package models
// assert.Equal(t, "NetID is not editable", err.Error())
// t.Log(err, Range, local)
// })
// t.Run("LocalRange", func(t *testing.T) {
// var networkupdate models.Network
// //NetID needs to be set as it will be in updateNetwork
// networkupdate.NetID = "skynet"
// networkupdate.LocalRange = "192.168.0.1/24"
// Range, local, err := network.Update(&networkupdate)
// assert.Nil(t, err)
// t.Log(err, Range, local)
// })
//}

View File

@@ -15,7 +15,7 @@ const (
// NODE_SERVER_NAME - the default server name
NODE_SERVER_NAME = "netmaker"
// TEN_YEARS_IN_SECONDS - ten years in seconds
TEN_YEARS_IN_SECONDS = 300000000
TEN_YEARS_IN_SECONDS = 315670000000000000
// MAX_NAME_LENGTH - max name length of node
MAX_NAME_LENGTH = 62
// == ACTIONS == (can only be set by server)
@@ -66,8 +66,6 @@ type CommonNode struct {
Connected bool `json:"connected" yaml:"connected"`
Address net.IPNet `json:"address" yaml:"address"`
Address6 net.IPNet `json:"address6" yaml:"address6"`
PostUp string `json:"postup" yaml:"postup"`
PostDown string `json:"postdown" yaml:"postdown"`
Action string `json:"action" yaml:"action"`
LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"`
IsLocal bool `json:"islocal" yaml:"islocal"`
@@ -115,8 +113,6 @@ type LegacyNode struct {
ProxyListenPort int32 `json:"proxy_listen_port" bson:"proxy_listen_port" yaml:"proxy_listen_port" validate:"numeric,min=0,max=65535"`
PublicKey string `json:"publickey" bson:"publickey" yaml:"publickey" validate:"required,base64"`
Endpoint string `json:"endpoint" bson:"endpoint" yaml:"endpoint" validate:"required,ip"`
PostUp string `json:"postup" bson:"postup" yaml:"postup"`
PostDown string `json:"postdown" bson:"postdown" yaml:"postdown"`
AllowedIPs []string `json:"allowedips" bson:"allowedips" yaml:"allowedips"`
PersistentKeepalive int32 `json:"persistentkeepalive" bson:"persistentkeepalive" yaml:"persistentkeepalive" validate:"omitempty,numeric,max=1000"`
IsHub string `json:"ishub" bson:"ishub" yaml:"ishub" validate:"checkyesorno"`
@@ -150,7 +146,6 @@ type LegacyNode struct {
IsServer string `json:"isserver" bson:"isserver" yaml:"isserver" validate:"checkyesorno"`
Action string `json:"action" bson:"action" yaml:"action"`
IsLocal string `json:"islocal" bson:"islocal" yaml:"islocal" validate:"checkyesorno"`
LocalRange string `json:"localrange" bson:"localrange" yaml:"localrange"`
IPForwarding string `json:"ipforwarding" bson:"ipforwarding" yaml:"ipforwarding" validate:"checkyesorno"`
OS string `json:"os" bson:"os" yaml:"os"`
MTU int32 `json:"mtu" bson:"mtu" yaml:"mtu"`
@@ -197,13 +192,8 @@ func (node *Node) PrimaryAddress() string {
}
// Node.SetDefaultConnected
func (node *LegacyNode) SetDefaultConnected() {
if node.Connected == "" {
node.Connected = "yes"
}
if node.IsServer == "yes" {
node.Connected = "yes"
}
func (node *Node) SetDefaultConnected() {
node.Connected = true
}
// Node.SetDefaultACL
@@ -374,12 +364,6 @@ func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftable
if newNode.Address6.String() == "" {
newNode.Address6 = currentNode.Address6
}
if newNode.PostUp == "" {
newNode.PostUp = currentNode.PostUp
}
if newNode.PostDown == "" {
newNode.PostDown = currentNode.PostDown
}
if newNode.PersistentKeepalive < 0 {
newNode.PersistentKeepalive = currentNode.PersistentKeepalive
}
@@ -495,13 +479,6 @@ func (ln *LegacyNode) ConvertToNewNode() (*Host, *Node) {
host.HostPass = ln.Password
host.Name = ln.Name
host.ListenPort = int(ln.ListenPort)
if _, cidr, err := net.ParseCIDR(ln.LocalAddress); err == nil {
host.LocalRange = *cidr
} else {
if _, cidr, err := net.ParseCIDR(ln.LocalRange); err == nil {
host.LocalRange = *cidr
}
}
host.ProxyListenPort = int(ln.ProxyListenPort)
host.MTU = int(ln.MTU)
host.PublicKey, _ = wgtypes.ParseKey(ln.PublicKey)
@@ -540,8 +517,6 @@ func (ln *LegacyNode) ConvertToNewNode() (*Host, *Node) {
Mask: net.CIDRMask(128, 128),
}
}
node.PostUp = ln.PostUp
node.PostDown = ln.PostDown
node.Action = ln.Action
node.IsLocal = parseBool(ln.IsLocal)
node.IsEgressGateway = parseBool(ln.IsEgressGateway)
@@ -565,8 +540,6 @@ func (n *Node) Legacy(h *Host, s *ServerConfig, net *Network) *LegacyNode {
l.ProxyListenPort = int32(h.ProxyListenPort)
l.PublicKey = h.PublicKey.String()
l.Endpoint = h.EndpointIP.String()
l.PostUp = n.PostUp
l.PostDown = n.PostDown
//l.AllowedIPs =
l.AccessKey = ""
l.Interface = WIREGUARD_INTERFACE
@@ -594,7 +567,6 @@ func (n *Node) Legacy(h *Host, s *ServerConfig, net *Network) *LegacyNode {
l.DNSOn = formatBool(n.DNSOn)
l.Action = n.Action
l.IsLocal = formatBool(n.IsLocal)
l.LocalRange = h.LocalRange.String()
l.IPForwarding = formatBool(h.IPForwarding)
l.OS = h.OS
l.MTU = int32(h.MTU)

68
models/proxy.go Normal file
View File

@@ -0,0 +1,68 @@
package models
import (
"net"
"time"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// ProxyAction - type for proxy action
type ProxyAction string
const (
// default proxy port
NmProxyPort = 51722
// PersistentKeepaliveInterval - default keepalive for wg peer
DefaultPersistentKeepaliveInterval = time.Duration(time.Second * 20)
// ProxyUpdate - constant for proxy update action
ProxyUpdate ProxyAction = "PROXY_UPDATE"
// ProxyDeletePeers - constant for proxy delete peers action
ProxyDeletePeers ProxyAction = "PROXY_DELETE"
// ProxyDeleteAllPeers - constant for proxy delete all peers action
ProxyDeleteAllPeers ProxyAction = "PROXY_DELETE_ALL"
// NoProxy - constant for no ProxyAction
NoProxy ProxyAction = "NO_PROXY"
)
// RelayedConf - struct relayed peers config
type RelayedConf struct {
RelayedPeerEndpoint *net.UDPAddr `json:"relayed_peer_endpoint"`
RelayedPeerPubKey string `json:"relayed_peer_pub_key"`
Peers []wgtypes.PeerConfig `json:"relayed_peers"`
}
// PeerConf - struct for peer config in the network
type PeerConf struct {
Proxy bool `json:"proxy"`
PublicListenPort int32 `json:"public_listen_port"`
IsExtClient bool `json:"is_ext_client"`
Address net.IP `json:"address"`
ExtInternalIp net.IP `json:"ext_internal_ip"`
IsRelayed bool `json:"is_relayed"`
RelayedTo *net.UDPAddr `json:"relayed_to"`
}
// ProxyManagerPayload - struct for proxy manager payload
type ProxyManagerPayload struct {
Action ProxyAction `json:"action"`
InterfaceName string `json:"interface_name"`
Server string `json:"server"`
//WgAddr string `json:"wg_addr"`
Peers []wgtypes.PeerConfig `json:"peers"`
PeerMap map[string]PeerConf `json:"peer_map"`
IsIngress bool `json:"is_ingress"`
IsRelayed bool `json:"is_relayed"`
RelayedTo *net.UDPAddr `json:"relayed_to"`
IsRelay bool `json:"is_relay"`
RelayedPeerConf map[string]RelayedConf `json:"relayed_conf"`
}
// Metric - struct for metric data
type ProxyMetric struct {
NodeConnectionStatus map[string]bool `json:"node_connection_status"`
LastRecordedLatency uint64 `json:"last_recorded_latency"`
TrafficSent int64 `json:"traffic_sent"` // stored in MB
TrafficRecieved int64 `json:"traffic_recieved"` // stored in MB
}

View File

@@ -161,8 +161,6 @@ type EgressGatewayRequest struct {
NatEnabled string `json:"natenabled" bson:"natenabled"`
Ranges []string `json:"ranges" bson:"ranges"`
Interface string `json:"interface" bson:"interface"`
PostUp string `json:"postup" bson:"postup"`
PostDown string `json:"postdown" bson:"postdown"`
}
// RelayRequest - relay request struct

View File

@@ -29,14 +29,14 @@ func Ping(client mqtt.Client, msg mqtt.Message) {
}
node, err := logic.GetNodeByID(id)
if err != nil {
logger.Log(0, "mq-ping error getting node: ", err.Error())
logger.Log(3, "mq-ping error getting node: ", err.Error())
record, err := database.FetchRecord(database.NODES_TABLE_NAME, id)
if err != nil {
logger.Log(0, "error reading database ", err.Error())
logger.Log(3, "error reading database ", err.Error())
return
}
logger.Log(0, "record from database")
logger.Log(0, record)
logger.Log(3, "record from database")
logger.Log(3, record)
return
}
decrypted, decryptErr := decryptMsg(&node, msg.Payload())
@@ -141,11 +141,11 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
logger.Log(1, "error unmarshaling payload ", err.Error())
return
}
logger.Log(0, "recieved host update for host: ", id)
logger.Log(3, fmt.Sprintf("recieved host update: %s\n", hostUpdate.Host.ID.String()))
var sendPeerUpdate bool
switch hostUpdate.Action {
case models.UpdateHost:
sendPeerUpdate = updateHostFromClient(&hostUpdate.Host, currentHost)
sendPeerUpdate = logic.UpdateHostFromClient(&hostUpdate.Host, currentHost)
err := logic.UpsertHost(currentHost)
if err != nil {
logger.Log(0, "failed to update host: ", currentHost.ID.String(), err.Error())
@@ -168,6 +168,12 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
logger.Log(0, "failed to pulish peer update: ", err.Error())
}
}
if sendPeerUpdate {
err := PublishPeerUpdate()
if err != nil {
logger.Log(0, "failed to pulish peer update: ", err.Error())
}
}
// if servercfg.Is_EE && ifaceDelta {
// if err = logic.EnterpriseResetAllPeersFailovers(currentHost.ID.String(), currentHost.Network); err != nil {
// logger.Log(1, "failed to reset failover list during node update", currentHost.ID.String(), currentHost.Network)
@@ -177,37 +183,6 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
}(msg)
}
// used for updating host on server with update recieved from client
func updateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool) {
if newHost.ListenPort != 0 && currHost.ListenPort != newHost.ListenPort {
currHost.ListenPort = newHost.ListenPort
sendPeerUpdate = true
}
if newHost.ProxyListenPort != 0 && currHost.ProxyListenPort != newHost.ProxyListenPort {
currHost.ProxyListenPort = newHost.ProxyListenPort
sendPeerUpdate = true
}
if newHost.PublicListenPort != 0 && currHost.PublicListenPort != newHost.PublicListenPort {
currHost.PublicListenPort = newHost.PublicListenPort
sendPeerUpdate = true
}
if currHost.ProxyEnabled != newHost.ProxyEnabled {
currHost.ProxyEnabled = newHost.ProxyEnabled
sendPeerUpdate = true
}
if currHost.EndpointIP.String() != newHost.EndpointIP.String() {
currHost.EndpointIP = newHost.EndpointIP
sendPeerUpdate = true
}
currHost.DaemonInstalled = newHost.DaemonInstalled
currHost.Debug = newHost.Debug
currHost.Verbosity = newHost.Verbosity
currHost.Version = newHost.Version
currHost.Name = newHost.Name
return
}
// UpdateMetrics message Handler -- handles updates from client nodes for metrics
func UpdateMetrics(client mqtt.Client, msg mqtt.Message) {
if servercfg.Is_EE {
@@ -370,7 +345,7 @@ func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) boo
for _, node := range nodes {
if !newMetrics.Connectivity[node.ID.String()].Connected &&
len(newMetrics.Connectivity[node.ID.String()].NodeName) > 0 &&
node.Connected == true &&
node.Connected &&
len(node.FailoverNode) > 0 &&
!node.Failover {
newMetrics.FailoverPeers[node.ID.String()] = node.FailoverNode.String()

View File

@@ -6,12 +6,10 @@ import (
"fmt"
"time"
proxy_models "github.com/gravitl/netclient/nmproxy/models"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
"github.com/gravitl/netmaker/serverctl"
)
// PublishPeerUpdate --- determines and publishes a peer update to all the hosts
@@ -26,6 +24,7 @@ func PublishPeerUpdate() error {
return err
}
for _, host := range hosts {
host := host
err = PublishSingleHostUpdate(&host)
if err != nil {
logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error())
@@ -46,7 +45,7 @@ func PublishSingleHostUpdate(host *models.Host) error {
if err != nil {
return err
}
proxyUpdate.Action = proxy_models.ProxyUpdate
proxyUpdate.Action = models.ProxyUpdate
peerUpdate.ProxyUpdate = proxyUpdate
}
@@ -123,13 +122,7 @@ func sendPeers() {
var force bool
peer_force_send++
if peer_force_send == 5 {
// run iptables update to ensure gateways work correctly and mq is forwarded if containerized
if servercfg.ManageIPTables() != "off" {
serverctl.InitIPTables(false)
}
servercfg.SetHost()
force = true
peer_force_send = 0
err := logic.TimerCheckpoint() // run telemetry & log dumps if 24 hours has passed..
@@ -142,6 +135,7 @@ func sendPeers() {
for _, host := range hosts {
if force {
host := host
logger.Log(2, "sending scheduled peer update (5 min)")
err = PublishSingleHostUpdate(&host)
if err != nil {

View File

@@ -23,8 +23,6 @@ func IfaceDelta(currentNode *models.LegacyNode, newNode *models.LegacyNode) bool
newNode.PersistentKeepalive != currentNode.PersistentKeepalive ||
newNode.DNSOn != currentNode.DNSOn ||
newNode.Connected != currentNode.Connected ||
newNode.PostUp != currentNode.PostUp ||
newNode.PostDown != currentNode.PostDown ||
len(newNode.AllowedIPs) != len(currentNode.AllowedIPs) {
return true
}

Binary file not shown.

View File

@@ -1,45 +0,0 @@
package common
import (
"log"
"os/exec"
"strings"
"github.com/gravitl/netmaker/nm-proxy/models"
)
var IsHostNetwork bool
var IsRelay bool
var IsIngressGateway bool
var IsRelayed bool
var IsServer bool
var InterfaceName string
var BehindNAT bool
var WgIfaceMap = models.WgIfaceConf{
Iface: nil,
PeerMap: make(map[string]*models.Conn),
}
var PeerKeyHashMap = make(map[string]models.RemotePeer)
//var WgIfaceKeyMap = make(map[string]models.RemotePeer)
var RelayPeerMap = make(map[string]map[string]models.RemotePeer)
var ExtClientsWaitTh = make(map[string]models.ExtClientPeer)
var ExtSourceIpMap = make(map[string]models.RemotePeer)
// RunCmd - runs a local command
func RunCmd(command string, printerr bool) (string, error) {
args := strings.Fields(command)
cmd := exec.Command(args[0], args[1:]...)
cmd.Wait()
out, err := cmd.CombinedOutput()
if err != nil && printerr {
log.Println("error running command: ", command)
log.Println(strings.TrimSuffix(string(out), "\n"))
}
return string(out), err
}

View File

@@ -1,22 +0,0 @@
package common
import (
"github.com/gravitl/netmaker/nm-proxy/models"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
func GetPeer(peerKey wgtypes.Key) (*models.Conn, bool) {
var peerInfo *models.Conn
var found bool
peerInfo, found = WgIfaceMap.PeerMap[peerKey.String()]
peerInfo.Mutex.RLock()
defer peerInfo.Mutex.RUnlock()
return peerInfo, found
}
func UpdatePeer(peer *models.Conn) {
peer.Mutex.Lock()
defer peer.Mutex.Unlock()
WgIfaceMap.PeerMap[peer.Key.String()] = peer
}

View File

@@ -1,424 +0,0 @@
package manager
import (
"context"
"crypto/md5"
"errors"
"fmt"
"log"
"net"
"reflect"
"runtime"
"github.com/gravitl/netmaker/nm-proxy/common"
"github.com/gravitl/netmaker/nm-proxy/models"
peerpkg "github.com/gravitl/netmaker/nm-proxy/peer"
"github.com/gravitl/netmaker/nm-proxy/wg"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
/*
TODO:-
1. ON Ingress node
--> for attached ext clients
-> start sniffer (will recieve pkts from ext clients (add ebf filter to listen on only ext traffic) if not intended to the interface forward it.)
-> start remote conn after endpoint is updated
-->
*/
var sent bool
type ProxyAction string
type ManagerPayload struct {
InterfaceName string `json:"interface_name"`
WgAddr string `json:"wg_addr"`
Peers []wgtypes.PeerConfig `json:"peers"`
PeerMap map[string]PeerConf `json:"peer_map"`
IsRelayed bool `json:"is_relayed"`
IsIngress bool `json:"is_ingress"`
RelayedTo *net.UDPAddr `json:"relayed_to"`
IsRelay bool `json:"is_relay"`
RelayedPeerConf map[string]RelayedConf `json:"relayed_conf"`
}
type RelayedConf struct {
RelayedPeerEndpoint *net.UDPAddr `json:"relayed_peer_endpoint"`
RelayedPeerPubKey string `json:"relayed_peer_pub_key"`
Peers []wgtypes.PeerConfig `json:"relayed_peers"`
}
type PeerConf struct {
IsExtClient bool `json:"is_ext_client"`
Address string `json:"address"`
IsAttachedExtClient bool `json:"is_attached_ext_client"`
IngressGatewayEndPoint *net.UDPAddr `json:"ingress_gateway_endpoint"`
IsRelayed bool `json:"is_relayed"`
RelayedTo *net.UDPAddr `json:"relayed_to"`
Proxy bool `json:"proxy"`
}
const (
AddInterface ProxyAction = "ADD_INTERFACE"
DeleteInterface ProxyAction = "DELETE_INTERFACE"
)
type ManagerAction struct {
Action ProxyAction
Payload ManagerPayload
}
func StartProxyManager(manageChan chan *ManagerAction) {
for {
select {
case mI := <-manageChan:
log.Printf("-------> PROXY-MANAGER: %+v\n", mI)
switch mI.Action {
case AddInterface:
mI.SetIngressGateway()
err := mI.AddInterfaceToProxy()
if err != nil {
log.Printf("failed to add interface: [%s] to proxy: %v\n ", mI.Payload.InterfaceName, err)
}
case DeleteInterface:
mI.DeleteInterface()
}
}
}
}
func (m *ManagerAction) DeleteInterface() {
var err error
if runtime.GOOS == "darwin" {
m.Payload.InterfaceName, err = wg.GetRealIface(m.Payload.InterfaceName)
if err != nil {
log.Println("failed to get real iface: ", err)
return
}
}
if common.WgIfaceMap.Iface.Name == m.Payload.InterfaceName {
cleanUpInterface()
}
}
func (m *ManagerAction) RelayUpdate() {
common.IsRelay = m.Payload.IsRelay
}
func (m *ManagerAction) SetIngressGateway() {
common.IsIngressGateway = m.Payload.IsIngress
}
func (m *ManagerAction) RelayPeers() {
common.IsRelay = true
for relayedNodePubKey, relayedNodeConf := range m.Payload.RelayedPeerConf {
relayedNodePubKeyHash := fmt.Sprintf("%x", md5.Sum([]byte(relayedNodePubKey)))
if _, ok := common.RelayPeerMap[relayedNodePubKeyHash]; !ok {
common.RelayPeerMap[relayedNodePubKeyHash] = make(map[string]models.RemotePeer)
}
for _, peer := range relayedNodeConf.Peers {
if peer.Endpoint != nil {
peer.Endpoint.Port = models.NmProxyPort
remotePeerKeyHash := fmt.Sprintf("%x", md5.Sum([]byte(peer.PublicKey.String())))
common.RelayPeerMap[relayedNodePubKeyHash][remotePeerKeyHash] = models.RemotePeer{
Endpoint: peer.Endpoint,
}
}
}
relayedNodeConf.RelayedPeerEndpoint.Port = models.NmProxyPort
common.RelayPeerMap[relayedNodePubKeyHash][relayedNodePubKeyHash] = models.RemotePeer{
Endpoint: relayedNodeConf.RelayedPeerEndpoint,
}
}
}
func cleanUpInterface() {
log.Println("########------------> CLEANING UP: ", common.WgIfaceMap.Iface.Name)
for _, peerI := range common.WgIfaceMap.PeerMap {
peerI.Mutex.Lock()
peerI.StopConn()
peerI.Mutex.Unlock()
delete(common.WgIfaceMap.PeerMap, peerI.Key.String())
}
common.WgIfaceMap.PeerMap = make(map[string]*models.Conn)
}
func (m *ManagerAction) processPayload() (*wg.WGIface, error) {
var err error
var wgIface *wg.WGIface
if m.Payload.InterfaceName == "" {
return nil, errors.New("interface cannot be empty")
}
if len(m.Payload.Peers) == 0 {
return nil, errors.New("no peers to add")
}
if runtime.GOOS == "darwin" {
m.Payload.InterfaceName, err = wg.GetRealIface(m.Payload.InterfaceName)
if err != nil {
log.Println("failed to get real iface: ", err)
}
}
common.InterfaceName = m.Payload.InterfaceName
wgIface, err = wg.NewWGIFace(m.Payload.InterfaceName, "127.0.0.1/32", wg.DefaultMTU)
if err != nil {
log.Println("Failed init new interface: ", err)
return nil, err
}
if common.WgIfaceMap.Iface == nil {
for i := len(m.Payload.Peers) - 1; i >= 0; i-- {
if !m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].Proxy {
log.Println("-----------> skipping peer, proxy is off: ", m.Payload.Peers[i].PublicKey)
if err := wgIface.Update(m.Payload.Peers[i], false); err != nil {
log.Println("falied to update peer: ", err)
}
m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
continue
}
}
common.WgIfaceMap.Iface = wgIface.Device
common.WgIfaceMap.IfaceKeyHash = fmt.Sprintf("%x", md5.Sum([]byte(wgIface.Device.PublicKey.String())))
return wgIface, nil
}
wgProxyConf := common.WgIfaceMap
if m.Payload.IsRelay {
m.RelayPeers()
}
common.IsRelay = m.Payload.IsRelay
// check if node is getting relayed
if common.IsRelayed != m.Payload.IsRelayed {
common.IsRelayed = m.Payload.IsRelayed
cleanUpInterface()
return wgIface, nil
}
// sync map with wg device config
// check if listen port has changed
if wgIface.Device.ListenPort != wgProxyConf.Iface.ListenPort {
// reset proxy for this interface
cleanUpInterface()
return wgIface, nil
}
// check device conf different from proxy
wgProxyConf.Iface = wgIface.Device
// sync peer map with new update
for _, currPeerI := range wgProxyConf.Iface.Peers {
if _, ok := m.Payload.PeerMap[currPeerI.PublicKey.String()]; !ok {
if val, ok := wgProxyConf.PeerMap[currPeerI.PublicKey.String()]; ok {
val.Mutex.Lock()
if val.IsAttachedExtClient {
log.Println("------> Deleting ExtClient Watch Thread: ", currPeerI.PublicKey.String())
if val, ok := common.ExtClientsWaitTh[currPeerI.PublicKey.String()]; ok {
val.CancelFunc()
delete(common.ExtClientsWaitTh, currPeerI.PublicKey.String())
}
log.Println("-----> Deleting Ext Client from Src Ip Map: ", currPeerI.PublicKey.String())
delete(common.ExtSourceIpMap, val.Config.PeerConf.Endpoint.String())
}
val.StopConn()
val.Mutex.Unlock()
delete(wgProxyConf.PeerMap, currPeerI.PublicKey.String())
}
// delete peer from interface
log.Println("CurrPeer Not Found, Deleting Peer from Interface: ", currPeerI.PublicKey.String())
if err := wgIface.RemovePeer(currPeerI.PublicKey.String()); err != nil {
log.Println("failed to remove peer: ", currPeerI.PublicKey.String(), err)
}
delete(common.PeerKeyHashMap, fmt.Sprintf("%x", md5.Sum([]byte(currPeerI.PublicKey.String()))))
}
}
for i := len(m.Payload.Peers) - 1; i >= 0; i-- {
if currentPeer, ok := wgProxyConf.PeerMap[m.Payload.Peers[i].PublicKey.String()]; ok {
currentPeer.Mutex.Lock()
if currentPeer.IsAttachedExtClient {
m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
continue
}
// check if proxy is off for the peer
if !m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].Proxy {
// cleanup proxy connections for the peer
currentPeer.StopConn()
delete(wgProxyConf.PeerMap, currentPeer.Key.String())
// update the peer with actual endpoint
if err := wgIface.Update(m.Payload.Peers[i], false); err != nil {
log.Println("falied to update peer: ", err)
}
m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
continue
}
// check if peer is not connected to proxy
devPeer, err := wg.GetPeer(m.Payload.InterfaceName, currentPeer.Key.String())
if err == nil {
log.Printf("---------> COMAPRING ENDPOINT: DEV: %s, Proxy: %s", devPeer.Endpoint.String(), currentPeer.Config.LocalConnAddr.String())
if devPeer.Endpoint.String() != currentPeer.Config.LocalConnAddr.String() {
log.Println("---------> endpoint is not set to proxy: ", currentPeer.Key)
currentPeer.StopConn()
delete(wgProxyConf.PeerMap, currentPeer.Key.String())
continue
}
}
//check if peer is being relayed
if currentPeer.IsRelayed != m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].IsRelayed {
log.Println("---------> peer relay status has been changed: ", currentPeer.Key)
currentPeer.StopConn()
delete(wgProxyConf.PeerMap, currentPeer.Key.String())
continue
}
// check if relay endpoint has been changed
if currentPeer.RelayedEndpoint != nil &&
m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].RelayedTo != nil &&
currentPeer.RelayedEndpoint.String() != m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].RelayedTo.String() {
log.Println("---------> peer relay endpoint has been changed: ", currentPeer.Key)
currentPeer.StopConn()
delete(wgProxyConf.PeerMap, currentPeer.Key.String())
continue
}
if !reflect.DeepEqual(m.Payload.Peers[i], *currentPeer.Config.PeerConf) {
if currentPeer.Config.RemoteConnAddr.IP.String() != m.Payload.Peers[i].Endpoint.IP.String() {
log.Println("----------> Resetting proxy for Peer: ", currentPeer.Key, m.Payload.InterfaceName)
currentPeer.StopConn()
currentPeer.Mutex.Unlock()
delete(wgProxyConf.PeerMap, currentPeer.Key.String())
continue
} else {
log.Println("----->##### Updating Peer on Interface: ", m.Payload.InterfaceName, currentPeer.Key)
updatePeerConf := m.Payload.Peers[i]
localUdpAddr, err := net.ResolveUDPAddr("udp", currentPeer.Config.LocalConnAddr.String())
if err == nil {
updatePeerConf.Endpoint = localUdpAddr
}
if err := wgIface.Update(updatePeerConf, true); err != nil {
log.Println("failed to update peer: ", currentPeer.Key, err)
}
currentPeer.Config.PeerConf = &m.Payload.Peers[i]
wgProxyConf.PeerMap[currentPeer.Key.String()] = currentPeer
// delete the peer from the list
log.Println("-----------> deleting peer from list: ", m.Payload.Peers[i].PublicKey)
m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
}
} else {
// delete the peer from the list
log.Println("-----------> No updates observed so deleting peer: ", m.Payload.Peers[i].PublicKey)
m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
}
currentPeer.Mutex.Unlock()
} else if !m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].Proxy && !m.Payload.PeerMap[m.Payload.Peers[i].PublicKey.String()].IsAttachedExtClient {
log.Println("-----------> skipping peer, proxy is off: ", m.Payload.Peers[i].PublicKey)
if err := wgIface.Update(m.Payload.Peers[i], false); err != nil {
log.Println("falied to update peer: ", err)
}
m.Payload.Peers = append(m.Payload.Peers[:i], m.Payload.Peers[i+1:]...)
}
}
// sync dev peers with new update
common.WgIfaceMap = wgProxyConf
log.Println("CLEANED UP..........")
return wgIface, nil
}
func (m *ManagerAction) AddInterfaceToProxy() error {
var err error
wgInterface, err := m.processPayload()
if err != nil {
return err
}
log.Printf("wg: %+v\n", wgInterface)
for _, peerI := range m.Payload.Peers {
peerConf := m.Payload.PeerMap[peerI.PublicKey.String()]
if peerI.Endpoint == nil && !(peerConf.IsAttachedExtClient || peerConf.IsExtClient) {
log.Println("Endpoint nil for peer: ", peerI.PublicKey.String())
continue
}
if peerConf.IsExtClient && !peerConf.IsAttachedExtClient {
peerI.Endpoint = peerConf.IngressGatewayEndPoint
}
var isRelayed bool
var relayedTo *net.UDPAddr
if m.Payload.IsRelayed {
isRelayed = true
relayedTo = m.Payload.RelayedTo
} else {
isRelayed = peerConf.IsRelayed
relayedTo = peerConf.RelayedTo
}
if peerConf.IsAttachedExtClient {
log.Println("Extclient Thread...")
go func(wgInterface *wg.WGIface, peer *wgtypes.PeerConfig,
isRelayed bool, relayTo *net.UDPAddr, peerConf PeerConf, ingGwAddr string) {
addExtClient := false
commChan := make(chan *net.UDPAddr, 100)
ctx, cancel := context.WithCancel(context.Background())
common.ExtClientsWaitTh[peerI.PublicKey.String()] = models.ExtClientPeer{
CancelFunc: cancel,
CommChan: commChan,
}
defer func() {
if addExtClient {
log.Println("GOT ENDPOINT for Extclient adding peer...")
common.ExtSourceIpMap[peer.Endpoint.String()] = models.RemotePeer{
Interface: wgInterface.Name,
PeerKey: peer.PublicKey.String(),
IsExtClient: peerConf.IsExtClient,
IsAttachedExtClient: peerConf.IsAttachedExtClient,
Endpoint: peer.Endpoint,
}
peerpkg.AddNewPeer(wgInterface, peer, peerConf.Address, isRelayed,
peerConf.IsExtClient, peerConf.IsAttachedExtClient, relayedTo)
}
log.Println("Exiting extclient watch Thread for: ", peer.PublicKey.String())
}()
for {
select {
case <-ctx.Done():
return
case endpoint := <-commChan:
if endpoint != nil {
addExtClient = true
peer.Endpoint = endpoint
delete(common.ExtClientsWaitTh, peer.PublicKey.String())
return
}
}
}
}(wgInterface, &peerI, isRelayed, relayedTo, peerConf, m.Payload.WgAddr)
continue
}
peerpkg.AddNewPeer(wgInterface, &peerI, peerConf.Address, isRelayed,
peerConf.IsExtClient, peerConf.IsAttachedExtClient, relayedTo)
}
return nil
}

View File

@@ -1,56 +0,0 @@
package metrics
import (
"encoding/json"
"os"
"sync"
"time"
)
/*
1. Create metrics packet--> packet with identifier to track latency, errors.
*/
type Metric struct {
LastRecordedLatency uint64
ConnectionStatus bool
TrafficSent float64
TrafficRecieved float64
}
type MetricsPayload struct {
MetricType MetricsUpdateType
Value interface{}
}
type MetricsUpdateType uint32
const (
LatencyUpdate MetricsUpdateType = 1
TrafficSentUpdate MetricsUpdateType = 2
TrafficRecievedUpdate MetricsUpdateType = 3
)
var MetricsMapLock = &sync.RWMutex{}
var MetricsMap = make(map[string]Metric)
func init() {
go func() {
for {
time.Sleep(1 * time.Minute)
PrintMetrics()
}
}()
}
func PrintMetrics() {
data, err := json.MarshalIndent(MetricsMap, "", " ")
if err != nil {
return
}
os.WriteFile("/tmp/metrics.json", data, 0755)
}

View File

@@ -1,65 +0,0 @@
package models
import (
"context"
"net"
"sync"
"time"
"github.com/gravitl/netmaker/nm-proxy/wg"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
const (
NmProxyPort = 51722
DefaultCIDR = "127.0.0.1/8"
)
type ProxyConfig struct {
RemoteKey wgtypes.Key
LocalKey wgtypes.Key
WgInterface *wg.WGIface
IsExtClient bool
PersistentKeepalive *time.Duration
RecieverChan chan []byte
PeerConf *wgtypes.PeerConfig
PeerEndpoint *net.UDPAddr
RemoteConnAddr *net.UDPAddr
LocalConnAddr *net.UDPAddr
}
// Conn is a peer Connection configuration
type Conn struct {
// Key is a public key of a remote peer
Key wgtypes.Key
IsExtClient bool
IsRelayed bool
RelayedEndpoint *net.UDPAddr
IsAttachedExtClient bool
Config ProxyConfig
StopConn func()
ResetConn func()
LocalConn net.Conn
Mutex *sync.RWMutex
}
type RemotePeer struct {
PeerKey string
Interface string
Endpoint *net.UDPAddr
IsExtClient bool
IsAttachedExtClient bool
LocalConn net.Conn
}
type ExtClientPeer struct {
CancelFunc context.CancelFunc
CommChan chan *net.UDPAddr
}
type WgIfaceConf struct {
Iface *wgtypes.Device
IfaceKeyHash string
PeerMap map[string]*Conn
}

View File

@@ -1,47 +0,0 @@
package nmproxy
import (
"context"
"log"
"net"
"os"
"github.com/gravitl/netmaker/nm-proxy/common"
"github.com/gravitl/netmaker/nm-proxy/manager"
"github.com/gravitl/netmaker/nm-proxy/server"
"github.com/gravitl/netmaker/nm-proxy/stun"
)
/*
TODO:
1. Mutex locks for maps
2. CRUD funcs on Maps
3. Comments
*/
func Start(ctx context.Context, mgmChan chan *manager.ManagerAction, apiServerAddr string) {
log.Println("Starting Proxy...")
common.IsHostNetwork = (os.Getenv("HOST_NETWORK") == "" || os.Getenv("HOST_NETWORK") == "on")
hInfo := stun.GetHostInfo(apiServerAddr)
stun.Host = hInfo
log.Printf("HOSTINFO: %+v", hInfo)
if IsPublicIP(hInfo.PrivIp) {
log.Println("Host is public facing!!!")
}
// start the netclient proxy server
err := server.NmProxyServer.CreateProxyServer(0, 0, hInfo.PrivIp.String())
if err != nil {
log.Fatal("failed to create proxy: ", err)
}
go manager.StartProxyManager(mgmChan)
server.NmProxyServer.Listen(ctx)
}
// IsPublicIP indicates whether IP is public or not.
func IsPublicIP(ip net.IP) bool {
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsPrivate() {
return false
}
return true
}

View File

@@ -1,174 +0,0 @@
package packet
import (
"bytes"
"crypto/md5"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"log"
"net"
"time"
"github.com/gravitl/netmaker/nm-proxy/common"
"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/chacha20poly1305"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
func ConsumeHandshakeInitiationMsg(initiator bool, buf []byte, src *net.UDPAddr, devicePubKey NoisePublicKey, devicePrivKey NoisePrivateKey) error {
var (
hash [blake2s.Size]byte
chainKey [blake2s.Size]byte
)
var err error
var msg MessageInitiation
reader := bytes.NewReader(buf[:])
err = binary.Read(reader, binary.LittleEndian, &msg)
if err != nil {
log.Println("Failed to decode initiation message")
return err
}
if msg.Type != MessageInitiationType {
return errors.New("not handshake initiation message")
}
log.Println("-----> ConsumeHandshakeInitiationMsg, Intitator: ", initiator)
mixHash(&hash, &InitialHash, devicePubKey[:])
mixHash(&hash, &hash, msg.Ephemeral[:])
mixKey(&chainKey, &InitialChainKey, msg.Ephemeral[:])
// decrypt static key
var peerPK NoisePublicKey
var key [chacha20poly1305.KeySize]byte
ss := sharedSecret(&devicePrivKey, msg.Ephemeral)
if isZero(ss[:]) {
return errors.New("no secret")
}
KDF2(&chainKey, &key, chainKey[:], ss[:])
aead, _ := chacha20poly1305.New(key[:])
_, err = aead.Open(peerPK[:0], ZeroNonce[:], msg.Static[:], hash[:])
if err != nil {
return err
}
log.Println("--------> Got HandShake from peer: ", base64.StdEncoding.EncodeToString(peerPK[:]), src)
if val, ok := common.ExtClientsWaitTh[base64.StdEncoding.EncodeToString(peerPK[:])]; ok {
val.CommChan <- src
time.Sleep(time.Second * 3)
}
setZero(hash[:])
setZero(chainKey[:])
return nil
}
func CreateProxyUpdatePacket(msg *ProxyUpdateMessage) ([]byte, error) {
var buff [MessageProxyUpdateSize]byte
writer := bytes.NewBuffer(buff[:0])
err := binary.Write(writer, binary.LittleEndian, msg)
if err != nil {
return nil, err
}
packet := writer.Bytes()
return packet, nil
}
func ConsumeProxyUpdateMsg(buf []byte) (*ProxyUpdateMessage, error) {
var msg ProxyUpdateMessage
reader := bytes.NewReader(buf[:])
err := binary.Read(reader, binary.LittleEndian, &msg)
if err != nil {
log.Println("Failed to decode proxy update message")
return nil, err
}
if msg.Type != MessageProxyUpdateType {
return nil, errors.New("not proxy update message")
}
return &msg, nil
}
func CreateMetricPacket(id uint32, sender, reciever wgtypes.Key) ([]byte, error) {
msg := MetricMessage{
Type: MessageMetricsType,
ID: id,
Sender: sender,
Reciever: reciever,
TimeStamp: time.Now().UnixMilli(),
}
log.Printf("----------> $$$$$$ CREATED PACKET: %+v\n", msg)
var buff [MessageMetricSize]byte
writer := bytes.NewBuffer(buff[:0])
err := binary.Write(writer, binary.LittleEndian, msg)
if err != nil {
return nil, err
}
packet := writer.Bytes()
return packet, nil
}
func ConsumeMetricPacket(buf []byte) (*MetricMessage, error) {
var msg MetricMessage
var err error
reader := bytes.NewReader(buf[:])
err = binary.Read(reader, binary.LittleEndian, &msg)
if err != nil {
log.Println("Failed to decode metric message")
return nil, err
}
if msg.Type != MessageMetricsType {
return nil, errors.New("not metric message")
}
return &msg, nil
}
func ProcessPacketBeforeSending(buf []byte, n int, srckey, dstKey string) ([]byte, int, string, string) {
srcKeymd5 := md5.Sum([]byte(srckey))
dstKeymd5 := md5.Sum([]byte(dstKey))
m := ProxyMessage{
Type: MessageProxyType,
Sender: srcKeymd5,
Reciever: dstKeymd5,
}
var msgBuffer [MessageProxySize]byte
writer := bytes.NewBuffer(msgBuffer[:0])
err := binary.Write(writer, binary.LittleEndian, m)
if err != nil {
log.Println(err)
}
if n > len(buf)-MessageProxySize {
buf = append(buf, msgBuffer[:]...)
} else {
copy(buf[n:n+MessageProxySize], msgBuffer[:])
}
n += MessageProxySize
return buf, n, fmt.Sprintf("%x", srcKeymd5), fmt.Sprintf("%x", dstKeymd5)
}
func ExtractInfo(buffer []byte, n int) (int, string, string, error) {
data := buffer[:n]
if len(data) < MessageProxySize {
return n, "", "", errors.New("proxy message not found")
}
var msg ProxyMessage
var err error
reader := bytes.NewReader(buffer[n-MessageProxySize:])
err = binary.Read(reader, binary.LittleEndian, &msg)
if err != nil {
log.Println("Failed to decode proxy message")
return n, "", "", err
}
if msg.Type != MessageProxyType {
return n, "", "", errors.New("not a proxy message")
}
n -= MessageProxySize
return n, fmt.Sprintf("%x", msg.Sender), fmt.Sprintf("%x", msg.Reciever), nil
}

View File

@@ -1,52 +0,0 @@
package packet
import (
"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/poly1305"
"golang.zx2c4.com/wireguard/tai64n"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
var (
InitialChainKey [blake2s.Size]byte
InitialHash [blake2s.Size]byte
ZeroNonce [chacha20poly1305.NonceSize]byte
)
func init() {
InitialChainKey = blake2s.Sum256([]byte(NoiseConstruction))
mixHash(&InitialHash, &InitialChainKey, []byte(WGIdentifier))
}
type MessageInitiation struct {
Type MessageType
Sender uint32
Ephemeral NoisePublicKey
Static [NoisePublicKeySize + poly1305.TagSize]byte
Timestamp [tai64n.TimestampSize + poly1305.TagSize]byte
MAC1 [blake2s.Size128]byte
MAC2 [blake2s.Size128]byte
}
type MetricMessage struct {
Type MessageType
ID uint32
Sender wgtypes.Key
Reciever wgtypes.Key
TimeStamp int64
}
type ProxyMessage struct {
Type MessageType
Sender [16]byte
Reciever [16]byte
}
type ProxyUpdateMessage struct {
Type MessageType
Action ProxyActionType
Sender wgtypes.Key
Reciever wgtypes.Key
ListenPort uint32
}

View File

@@ -1,113 +0,0 @@
package packet
import (
"crypto/hmac"
"crypto/subtle"
"hash"
"github.com/gravitl/netmaker/nm-proxy/wg"
"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/curve25519"
)
type MessageType uint32
type ProxyActionType uint32
const (
MessageInitiationType MessageType = 1
MessageMetricsType MessageType = 5
MessageProxyType MessageType = 6
MessageProxyUpdateType MessageType = 7
)
const (
UpdateListenPort ProxyActionType = 1
)
const (
NoisePublicKeySize = 32
NoisePrivateKeySize = 32
MessageMetricSize = 148
MessageProxyUpdateSize = 148
MessageProxySize = 36
NoiseConstruction = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
WGIdentifier = "WireGuard v1 zx2c4 Jason@zx2c4.com"
WGLabelMAC1 = "mac1----"
WGLabelCookie = "cookie--"
)
func mixKey(dst, c *[blake2s.Size]byte, data []byte) {
KDF1(dst, c[:], data)
}
func mixHash(dst, h *[blake2s.Size]byte, data []byte) {
hash, _ := blake2s.New256(nil)
hash.Write(h[:])
hash.Write(data)
hash.Sum(dst[:0])
hash.Reset()
}
func HMAC1(sum *[blake2s.Size]byte, key, in0 []byte) {
mac := hmac.New(func() hash.Hash {
h, _ := blake2s.New256(nil)
return h
}, key)
mac.Write(in0)
mac.Sum(sum[:0])
}
func HMAC2(sum *[blake2s.Size]byte, key, in0, in1 []byte) {
mac := hmac.New(func() hash.Hash {
h, _ := blake2s.New256(nil)
return h
}, key)
mac.Write(in0)
mac.Write(in1)
mac.Sum(sum[:0])
}
func KDF1(t0 *[blake2s.Size]byte, key, input []byte) {
HMAC1(t0, key, input)
HMAC1(t0, t0[:], []byte{0x1})
}
func KDF2(t0, t1 *[blake2s.Size]byte, key, input []byte) {
var prk [blake2s.Size]byte
HMAC1(&prk, key, input)
HMAC1(t0, prk[:], []byte{0x1})
HMAC2(t1, prk[:], t0[:], []byte{0x2})
setZero(prk[:])
}
func setZero(arr []byte) {
for i := range arr {
arr[i] = 0
}
}
func isZero(val []byte) bool {
acc := 1
for _, b := range val {
acc &= subtle.ConstantTimeByteEq(b, 0)
}
return acc == 1
}
func GetDeviceKeys(ifaceName string) (NoisePrivateKey, NoisePublicKey, error) {
wgPrivKey := wg.GetWgIfacePrivKey(ifaceName)
wgPubKey := wg.GetWgIfacePubKey(ifaceName)
return wgPrivKey, wgPubKey, nil
}
type (
NoisePublicKey [NoisePublicKeySize]byte
NoisePrivateKey [NoisePrivateKeySize]byte
)
func sharedSecret(sk *NoisePrivateKey, pk NoisePublicKey) (ss [NoisePublicKeySize]byte) {
apk := (*[NoisePublicKeySize]byte)(&pk)
ask := (*[NoisePrivateKeySize]byte)(sk)
curve25519.ScalarMult(&ss, ask, apk)
return ss
}

View File

@@ -1,83 +0,0 @@
package peer
import (
"crypto/md5"
"errors"
"fmt"
"log"
"net"
"sync"
"time"
"github.com/gravitl/netmaker/nm-proxy/common"
"github.com/gravitl/netmaker/nm-proxy/models"
"github.com/gravitl/netmaker/nm-proxy/proxy"
"github.com/gravitl/netmaker/nm-proxy/wg"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
func AddNewPeer(wgInterface *wg.WGIface, peer *wgtypes.PeerConfig, peerAddr string,
isRelayed, isExtClient, isAttachedExtClient bool, relayTo *net.UDPAddr) error {
if peer.PersistentKeepaliveInterval == nil {
d := time.Second * 25
peer.PersistentKeepaliveInterval = &d
}
c := models.ProxyConfig{
LocalKey: wgInterface.Device.PublicKey,
RemoteKey: peer.PublicKey,
WgInterface: wgInterface,
IsExtClient: isExtClient,
PeerConf: peer,
PersistentKeepalive: peer.PersistentKeepaliveInterval,
RecieverChan: make(chan []byte, 1000),
}
p := proxy.NewProxy(c)
peerPort := models.NmProxyPort
if isExtClient && isAttachedExtClient {
peerPort = peer.Endpoint.Port
}
peerEndpointIP := peer.Endpoint.IP
if isRelayed {
//go server.NmProxyServer.KeepAlive(peer.Endpoint.IP.String(), common.NmProxyPort)
if relayTo == nil {
return errors.New("relay endpoint is nil")
}
peerEndpointIP = relayTo.IP
}
peerEndpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", peerEndpointIP, peerPort))
if err != nil {
return err
}
p.Config.PeerEndpoint = peerEndpoint
log.Printf("Starting proxy for Peer: %s\n", peer.PublicKey.String())
err = p.Start()
if err != nil {
return err
}
connConf := models.Conn{
Mutex: &sync.RWMutex{},
Key: peer.PublicKey,
IsRelayed: isRelayed,
RelayedEndpoint: relayTo,
IsAttachedExtClient: isAttachedExtClient,
Config: p.Config,
StopConn: p.Close,
ResetConn: p.Reset,
LocalConn: p.LocalConn,
}
common.WgIfaceMap.PeerMap[peer.PublicKey.String()] = &connConf
common.PeerKeyHashMap[fmt.Sprintf("%x", md5.Sum([]byte(peer.PublicKey.String())))] = models.RemotePeer{
Interface: wgInterface.Name,
PeerKey: peer.PublicKey.String(),
IsExtClient: isExtClient,
Endpoint: peerEndpoint,
IsAttachedExtClient: isAttachedExtClient,
LocalConn: p.LocalConn,
}
return nil
}

View File

@@ -1,266 +0,0 @@
package proxy
import (
"context"
"errors"
"fmt"
"log"
"net"
"runtime"
"github.com/gravitl/netmaker/nm-proxy/common"
"github.com/gravitl/netmaker/nm-proxy/models"
)
// Proxy - WireguardProxy proxies
type Proxy struct {
Ctx context.Context
Cancel context.CancelFunc
Config models.ProxyConfig
RemoteConn *net.UDPAddr
LocalConn net.Conn
}
func (p *Proxy) Start() error {
var err error
p.RemoteConn = p.Config.PeerEndpoint
log.Printf("----> Established Remote Conn with RPeer: %s, ----> RAddr: %s", p.Config.RemoteKey.String(), p.RemoteConn.String())
addr, err := GetFreeIp(models.DefaultCIDR, p.Config.WgInterface.Port)
if err != nil {
log.Println("Failed to get freeIp: ", err)
return err
}
wgListenAddr, err := GetInterfaceListenAddr(p.Config.WgInterface.Port)
if err != nil {
log.Println("failed to get wg listen addr: ", err)
return err
}
if runtime.GOOS == "darwin" {
wgListenAddr.IP = net.ParseIP(addr)
}
p.LocalConn, err = net.DialUDP("udp", &net.UDPAddr{
IP: net.ParseIP(addr),
Port: models.NmProxyPort,
}, wgListenAddr)
if err != nil {
log.Printf("failed dialing to local Wireguard port,Err: %v\n", err)
return err
}
log.Printf("Dialing to local Wireguard port %s --> %s\n", p.LocalConn.LocalAddr().String(), p.LocalConn.RemoteAddr().String())
err = p.updateEndpoint()
if err != nil {
log.Printf("error while updating Wireguard peer endpoint [%s] %v\n", p.Config.RemoteKey, err)
return err
}
localAddr, err := net.ResolveUDPAddr("udp", p.LocalConn.LocalAddr().String())
if err != nil {
log.Println("failed to resolve local addr: ", err)
return err
}
p.Config.LocalConnAddr = localAddr
p.Config.RemoteConnAddr = p.RemoteConn
go p.ProxyPeer()
return nil
}
func (p *Proxy) Close() {
log.Println("------> Closing Proxy for ", p.Config.RemoteKey.String())
p.Cancel()
p.LocalConn.Close()
if runtime.GOOS == "darwin" {
host, _, err := net.SplitHostPort(p.LocalConn.LocalAddr().String())
if err != nil {
log.Println("Failed to split host: ", p.LocalConn.LocalAddr().String(), err)
return
}
if host != "127.0.0.1" {
_, err = common.RunCmd(fmt.Sprintf("ifconfig lo0 -alias %s 255.255.255.255", host), true)
if err != nil {
log.Println("Failed to add alias: ", err)
}
}
}
close(p.Config.RecieverChan)
}
func GetInterfaceListenAddr(port int) (*net.UDPAddr, error) {
locallistenAddr := "127.0.0.1"
udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", locallistenAddr, port))
if err != nil {
return udpAddr, err
}
if !common.IsHostNetwork {
addrs, err := getBoardCastAddress()
if err != nil {
return udpAddr, err
}
for _, addr := range addrs {
if liAddr := addr.(*net.IPNet).IP; liAddr != nil {
udpAddr.IP = liAddr
break
}
}
}
return udpAddr, nil
}
func getBoardCastAddress() ([]net.Addr, error) {
localnets, err := net.Interfaces()
if err != nil {
return nil, err
}
var (
ief net.Interface
addrs []net.Addr
)
for _, ief = range localnets {
if ief.Flags&net.FlagBroadcast != 0 && ief.Flags&net.FlagUp != 0 {
addrs, err = ief.Addrs()
if err == nil {
return addrs, nil
}
}
}
return nil, errors.New("couldn't obtain the broadcast addr")
}
// func StartSniffer(ctx context.Context, ifaceName, ingGwAddr, extClientAddr string, port int) {
// log.Println("Starting Packet Sniffer for iface: ", ifaceName)
// var (
// snapshotLen int32 = 1024
// promiscuous bool = false
// err error
// timeout time.Duration = 1 * time.Microsecond
// handle *pcap.Handle
// )
// // Open device
// handle, err = pcap.OpenLive(ifaceName, snapshotLen, promiscuous, timeout)
// if err != nil {
// log.Println("failed to start sniffer for iface: ", ifaceName, err)
// return
// }
// // if err := handle.SetBPFFilter(fmt.Sprintf("src %s and port %d", extClientAddr, port)); err != nil {
// // log.Println("failed to set bpf filter: ", err)
// // return
// // }
// defer handle.Close()
// // var tcp layers.TCP
// // var icmp layers.ICMPv4
// // var udp layers.UDP
// // parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &udp, &tcp, &icmp)
// packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
// for {
// select {
// case <-ctx.Done():
// log.Println("Stopping packet sniffer for iface: ", ifaceName, " port: ", port)
// return
// default:
// packet, err := packetSource.NextPacket()
// if err == nil {
// //processPkt(ifaceName, packet)
// ipLayer := packet.Layer(layers.LayerTypeIPv4)
// if ipLayer != nil {
// fmt.Println("IPv4 layer detected.")
// ip, _ := ipLayer.(*layers.IPv4)
// // IP layer variables:
// // Version (Either 4 or 6)
// // IHL (IP Header Length in 32-bit words)
// // TOS, Length, Id, Flags, FragOffset, TTL, Protocol (TCP?),
// // Checksum, SrcIP, DstIP
// fmt.Println("#########################")
// fmt.Printf("From %s to %s\n", ip.SrcIP, ip.DstIP)
// fmt.Println("Protocol: ", ip.Protocol.String())
// if (ip.SrcIP.String() == extClientAddr && ip.DstIP.String() != ingGwAddr) ||
// (ip.DstIP.String() == extClientAddr && ip.SrcIP.String() != ingGwAddr) {
// log.Println("-----> Fowarding PKT From: ", ip.SrcIP, " to: ", ip.DstIP)
// c, err := net.Dial("ip", ip.DstIP.String())
// if err == nil {
// c.Write(ip.Payload)
// c.Close()
// } else {
// log.Println("------> Failed to forward packet from sniffer: ", err)
// }
// }
// fmt.Println("#########################")
// }
// }
// }
// }
// }
// func processPkt(iface string, packet gopacket.Packet) {
// // Let's see if the packet is an ethernet packet
// // ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
// // if ethernetLayer != nil {
// // fmt.Println("Ethernet layer detected.")
// // ethernetPacket, _ := ethernetLayer.(*layers.Ethernet)
// // fmt.Println("Source MAC: ", ethernetPacket.SrcMAC)
// // fmt.Println("Destination MAC: ", ethernetPacket.DstMAC)
// // // Ethernet type is typically IPv4 but could be ARP or other
// // fmt.Println("Ethernet type: ", ethernetPacket.EthernetType)
// // fmt.Println()
// // }
// // Let's see if the packet is IP (even though the ether type told us)
// ipLayer := packet.Layer(layers.LayerTypeIPv4)
// if ipLayer != nil {
// fmt.Println("IPv4 layer detected.")
// ip, _ := ipLayer.(*layers.IPv4)
// // IP layer variables:
// // Version (Either 4 or 6)
// // IHL (IP Header Length in 32-bit words)
// // TOS, Length, Id, Flags, FragOffset, TTL, Protocol (TCP?),
// // Checksum, SrcIP, DstIP
// fmt.Printf("From %s to %s\n", ip.SrcIP, ip.DstIP)
// fmt.Println("Protocol: ", ip.Protocol)
// fmt.Println()
// }
// // udpLayer := packet.Layer(layers.LayerTypeUDP)
// // if udpLayer != nil {
// // udp, _ := udpLayer.(*layers.UDP)
// // fmt.Printf("UDP: From port %d to %d\n", udp.SrcPort, udp.DstPort)
// // fmt.Println()
// // }
// // // Iterate over all layers, printing out each layer type
// // fmt.Println("All packet layers:")
// // for _, layer := range packet.Layers() {
// // fmt.Println("- ", layer.LayerType())
// // }
// // When iterating through packet.Layers() above,
// // if it lists Payload layer then that is the same as
// // this applicationLayer. applicationLayer contains the payload
// // applicationLayer := packet.ApplicationLayer()
// // if applicationLayer != nil {
// // fmt.Println("Application layer/Payload found.")
// // fmt.Printf("%s\n", applicationLayer.Payload())
// // // Search for a string inside the payload
// // if strings.Contains(string(applicationLayer.Payload()), "HTTP") {
// // fmt.Println("HTTP found!")
// // }
// // }
// // Check for errors
// if err := packet.ErrorLayer(); err != nil {
// fmt.Println("Error decoding some part of the packet:", err)
// }
// }

View File

@@ -1,245 +0,0 @@
package proxy
import (
"context"
"errors"
"fmt"
"log"
"net"
"runtime"
"strings"
"sync"
"time"
"github.com/c-robinson/iplib"
"github.com/google/uuid"
"github.com/gravitl/netmaker/nm-proxy/common"
"github.com/gravitl/netmaker/nm-proxy/metrics"
"github.com/gravitl/netmaker/nm-proxy/models"
"github.com/gravitl/netmaker/nm-proxy/packet"
"github.com/gravitl/netmaker/nm-proxy/server"
"github.com/gravitl/netmaker/nm-proxy/stun"
"github.com/gravitl/netmaker/nm-proxy/wg"
)
func NewProxy(config models.ProxyConfig) *Proxy {
p := &Proxy{Config: config}
p.Ctx, p.Cancel = context.WithCancel(context.Background())
return p
}
func (p *Proxy) proxyToRemote(wg *sync.WaitGroup) {
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
buf := make([]byte, 65000)
defer wg.Done()
for {
select {
case <-p.Ctx.Done():
return
default:
n, err := p.LocalConn.Read(buf)
if err != nil {
log.Println("ERRR READ: ", err)
continue
}
// if _, found := common.GetPeer(p.Config.RemoteKey); !found {
// log.Printf("Peer: %s not found in config\n", p.Config.RemoteKey)
// p.Close()
// return
// }
go func(n int, peerKey string) {
metrics.MetricsMapLock.Lock()
metric := metrics.MetricsMap[peerKey]
metric.TrafficSent += float64(n) / (1 << 20)
metrics.MetricsMap[peerKey] = metric
metrics.MetricsMapLock.Unlock()
}(n, p.Config.RemoteKey.String())
//var srcPeerKeyHash, dstPeerKeyHash string
if !p.Config.IsExtClient {
buf, n, _, _ = packet.ProcessPacketBeforeSending(buf, n, p.Config.WgInterface.Device.PublicKey.String(), p.Config.RemoteKey.String())
if err != nil {
log.Println("failed to process pkt before sending: ", err)
}
}
// log.Printf("PROXING TO REMOTE!!!---> %s >>>>> %s >>>>> %s [[ SrcPeerHash: %s, DstPeerHash: %s ]]\n",
// p.LocalConn.LocalAddr(), server.NmProxyServer.Server.LocalAddr().String(), p.RemoteConn.String(), srcPeerKeyHash, dstPeerKeyHash)
_, err = server.NmProxyServer.Server.WriteToUDP(buf[:n], p.RemoteConn)
if err != nil {
log.Println("Failed to send to remote: ", err)
}
}
}
}
func (p *Proxy) Reset() {
p.Close()
if err := p.pullLatestConfig(); err != nil {
log.Println("couldn't perform reset: ", err)
return
}
p.Start()
}
func (p *Proxy) pullLatestConfig() error {
peer, found := common.GetPeer(p.Config.RemoteKey)
if found {
p.Config.PeerEndpoint.Port = peer.Config.PeerEndpoint.Port
} else {
return errors.New("peer not found")
}
return nil
}
func (p *Proxy) startMetricsThread(wg *sync.WaitGroup, rTicker *time.Ticker) {
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
defer wg.Done()
for {
select {
case <-p.Ctx.Done():
return
case <-ticker.C:
metrics.MetricsMapLock.Lock()
metric := metrics.MetricsMap[p.Config.RemoteKey.String()]
if metric.ConnectionStatus {
rTicker.Reset(*p.Config.PersistentKeepalive)
}
metric.ConnectionStatus = false
metrics.MetricsMap[p.Config.RemoteKey.String()] = metric
metrics.MetricsMapLock.Unlock()
pkt, err := packet.CreateMetricPacket(uuid.New().ID(), p.Config.LocalKey, p.Config.RemoteKey)
if err == nil {
log.Printf("-----------> ##### $$$$$ SENDING METRIC PACKET TO: %s\n", p.RemoteConn.String())
_, err = server.NmProxyServer.Server.WriteToUDP(pkt, p.RemoteConn)
if err != nil {
log.Println("Failed to send to metric pkt: ", err)
}
}
}
}
}
func (p *Proxy) peerUpdates(wg *sync.WaitGroup, ticker *time.Ticker) {
defer wg.Done()
for {
select {
case <-p.Ctx.Done():
return
case <-ticker.C:
// send listen port packet
m := &packet.ProxyUpdateMessage{
Type: packet.MessageProxyType,
Action: packet.UpdateListenPort,
Sender: p.Config.LocalKey,
Reciever: p.Config.RemoteKey,
ListenPort: uint32(stun.Host.PrivPort),
}
pkt, err := packet.CreateProxyUpdatePacket(m)
if err == nil {
log.Printf("-----------> ##### $$$$$ SENDING Proxy Update PACKET TO: %s\n", p.RemoteConn.String())
_, err = server.NmProxyServer.Server.WriteToUDP(pkt, p.RemoteConn)
if err != nil {
log.Println("Failed to send to metric pkt: ", err)
}
}
}
}
}
// ProxyPeer proxies everything from Wireguard to the RemoteKey peer and vice-versa
func (p *Proxy) ProxyPeer() {
ticker := time.NewTicker(*p.Config.PersistentKeepalive)
defer ticker.Stop()
wg := &sync.WaitGroup{}
wg.Add(1)
go p.proxyToRemote(wg)
// if common.BehindNAT {
wg.Add(1)
go p.startMetricsThread(wg, ticker)
wg.Add(1)
go p.peerUpdates(wg, ticker)
// }
wg.Wait()
}
func test(n int, buffer []byte) {
data := buffer[:n]
srcKeyHash := data[n-32 : n-16]
dstKeyHash := data[n-16:]
log.Printf("--------> TEST PACKET [ SRCKEYHASH: %x ], [ DSTKEYHASH: %x ] \n", srcKeyHash, dstKeyHash)
}
func (p *Proxy) updateEndpoint() error {
udpAddr, err := net.ResolveUDPAddr("udp", p.LocalConn.LocalAddr().String())
if err != nil {
return err
}
// add local proxy connection as a Wireguard peer
log.Printf("---> ####### Updating Peer: %+v\n", p.Config.PeerConf)
err = p.Config.WgInterface.UpdatePeer(p.Config.RemoteKey.String(), p.Config.PeerConf.AllowedIPs, wg.DefaultWgKeepAlive,
udpAddr, p.Config.PeerConf.PresharedKey)
if err != nil {
return err
}
return nil
}
func GetFreeIp(cidrAddr string, dstPort int) (string, error) {
//ensure AddressRange is valid
if dstPort == 0 {
return "", errors.New("dst port should be set")
}
if _, _, err := net.ParseCIDR(cidrAddr); err != nil {
log.Println("UniqueAddress encountered an error")
return "", err
}
net4 := iplib.Net4FromStr(cidrAddr)
newAddrs := net4.FirstAddress()
for {
if runtime.GOOS == "darwin" {
_, err := common.RunCmd(fmt.Sprintf("ifconfig lo0 alias %s 255.255.255.255", newAddrs.String()), true)
if err != nil {
log.Println("Failed to add alias: ", err)
}
}
conn, err := net.DialUDP("udp", &net.UDPAddr{
IP: net.ParseIP(newAddrs.String()),
Port: models.NmProxyPort,
}, &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: dstPort,
})
if err != nil {
log.Println("----> GetFreeIP ERR: ", err)
if strings.Contains(err.Error(), "can't assign requested address") ||
strings.Contains(err.Error(), "address already in use") || strings.Contains(err.Error(), "cannot assign requested address") {
var nErr error
newAddrs, nErr = net4.NextIP(newAddrs)
if nErr != nil {
return "", nErr
}
} else {
return "", err
}
}
if err == nil {
conn.Close()
return newAddrs.String(), nil
}
}
}

View File

@@ -1,285 +0,0 @@
package server
import (
"context"
"encoding/binary"
"fmt"
"log"
"net"
"time"
"github.com/gravitl/netmaker/nm-proxy/common"
"github.com/gravitl/netmaker/nm-proxy/metrics"
"github.com/gravitl/netmaker/nm-proxy/models"
"github.com/gravitl/netmaker/nm-proxy/packet"
)
var (
NmProxyServer = &ProxyServer{}
)
const (
defaultBodySize = 10000
defaultPort = models.NmProxyPort
)
type Config struct {
Port int
BodySize int
IsRelay bool
Addr net.Addr
}
type ProxyServer struct {
Config Config
Server *net.UDPConn
}
func (p *ProxyServer) Close() {
log.Println("--------->### Shutting down Proxy.....")
// clean up proxy connections
for _, peerI := range common.WgIfaceMap.PeerMap {
peerI.Mutex.Lock()
peerI.StopConn()
peerI.Mutex.Unlock()
}
// close server connection
NmProxyServer.Server.Close()
}
// Proxy.Listen - begins listening for packets
func (p *ProxyServer) Listen(ctx context.Context) {
// Buffer with indicated body size
buffer := make([]byte, 65036)
for {
select {
case <-ctx.Done():
p.Close()
return
default:
// Read Packet
n, source, err := p.Server.ReadFromUDP(buffer)
if err != nil || source == nil { // in future log errors?
log.Println("RECV ERROR: ", err)
continue
}
//go func(buffer []byte, source *net.UDPAddr, n int) {
proxyTransportMsg := true
var srcPeerKeyHash, dstPeerKeyHash string
n, srcPeerKeyHash, dstPeerKeyHash, err = packet.ExtractInfo(buffer, n)
if err != nil {
log.Println("proxy transport message not found: ", err)
proxyTransportMsg = false
}
if proxyTransportMsg {
p.proxyIncomingPacket(buffer[:], source, n, srcPeerKeyHash, dstPeerKeyHash)
continue
} else {
// unknown peer to proxy -> check if extclient and handle it
if handleExtClients(buffer[:], n, source) {
continue
}
}
handleMsgs(buffer, n, source)
}
}
}
func handleMsgs(buffer []byte, n int, source *net.UDPAddr) {
msgType := binary.LittleEndian.Uint32(buffer[:4])
switch packet.MessageType(msgType) {
case packet.MessageMetricsType:
metricMsg, err := packet.ConsumeMetricPacket(buffer[:n])
// calc latency
if err == nil {
log.Printf("------->$$$$$ Recieved Metric Pkt: %+v, FROM:%s\n", metricMsg, source.String())
if metricMsg.Sender == common.WgIfaceMap.Iface.PublicKey {
latency := time.Now().UnixMilli() - metricMsg.TimeStamp
metrics.MetricsMapLock.Lock()
metric := metrics.MetricsMap[metricMsg.Reciever.String()]
metric.LastRecordedLatency = uint64(latency)
metric.ConnectionStatus = true
metric.TrafficRecieved += float64(n) / (1 << 20)
metrics.MetricsMap[metricMsg.Reciever.String()] = metric
metrics.MetricsMapLock.Unlock()
} else if metricMsg.Reciever == common.WgIfaceMap.Iface.PublicKey {
// proxy it back to the sender
log.Println("------------> $$$ SENDING back the metric pkt to the source: ", source.String())
_, err = NmProxyServer.Server.WriteToUDP(buffer[:n], source)
if err != nil {
log.Println("Failed to send metric packet to remote: ", err)
}
metrics.MetricsMapLock.Lock()
metric := metrics.MetricsMap[metricMsg.Sender.String()]
metric.ConnectionStatus = true
metric.TrafficRecieved += float64(n) / (1 << 20)
metrics.MetricsMap[metricMsg.Sender.String()] = metric
metrics.MetricsMapLock.Unlock()
}
}
case packet.MessageProxyUpdateType:
msg, err := packet.ConsumeProxyUpdateMsg(buffer[:n])
if err == nil {
switch msg.Action {
case packet.UpdateListenPort:
if peer, ok := common.WgIfaceMap.PeerMap[msg.Sender.String()]; ok {
peer.Mutex.Lock()
if peer.Config.PeerEndpoint.Port != int(msg.ListenPort) {
// update peer conn
peer.Config.PeerEndpoint.Port = int(msg.ListenPort)
common.WgIfaceMap.PeerMap[msg.Sender.String()] = peer
log.Println("--------> Resetting Proxy Conn For Peer ", msg.Sender.String())
peer.Mutex.Unlock()
peer.ResetConn()
return
}
peer.Mutex.Unlock()
}
}
}
// consume handshake message for ext clients
case packet.MessageInitiationType:
err := packet.ConsumeHandshakeInitiationMsg(false, buffer[:n], source,
packet.NoisePublicKey(common.WgIfaceMap.Iface.PublicKey), packet.NoisePrivateKey(common.WgIfaceMap.Iface.PrivateKey))
if err != nil {
log.Println("---------> @@@ failed to decode HS: ", err)
}
}
}
func handleExtClients(buffer []byte, n int, source *net.UDPAddr) bool {
isExtClient := false
if peerInfo, ok := common.ExtSourceIpMap[source.String()]; ok {
if peerI, ok := common.WgIfaceMap.PeerMap[peerInfo.PeerKey]; ok {
peerI.Mutex.RLock()
peerI.Config.RecieverChan <- buffer[:n]
metrics.MetricsMapLock.Lock()
metric := metrics.MetricsMap[peerInfo.PeerKey]
metric.TrafficRecieved += float64(n) / (1 << 20)
metric.ConnectionStatus = true
metrics.MetricsMap[peerInfo.PeerKey] = metric
metrics.MetricsMapLock.Unlock()
peerI.Mutex.RUnlock()
isExtClient = true
}
}
return isExtClient
}
func (p *ProxyServer) proxyIncomingPacket(buffer []byte, source *net.UDPAddr, n int, srcPeerKeyHash, dstPeerKeyHash string) {
var err error
//log.Printf("--------> RECV PKT , [SRCKEYHASH: %s], SourceIP: [%s] \n", srcPeerKeyHash, source.IP.String())
if common.WgIfaceMap.IfaceKeyHash != dstPeerKeyHash && common.IsRelay {
log.Println("----------> Relaying######")
// check for routing map and forward to right proxy
if remoteMap, ok := common.RelayPeerMap[srcPeerKeyHash]; ok {
if conf, ok := remoteMap[dstPeerKeyHash]; ok {
log.Printf("--------> Relaying PKT [ SourceIP: %s:%d ], [ SourceKeyHash: %s ], [ DstIP: %s:%d ], [ DstHashKey: %s ] \n",
source.IP.String(), source.Port, srcPeerKeyHash, conf.Endpoint.String(), conf.Endpoint.Port, dstPeerKeyHash)
_, err = p.Server.WriteToUDP(buffer[:n+packet.MessageProxySize], conf.Endpoint)
if err != nil {
log.Println("Failed to send to remote: ", err)
}
return
}
} else {
if remoteMap, ok := common.RelayPeerMap[dstPeerKeyHash]; ok {
if conf, ok := remoteMap[dstPeerKeyHash]; ok {
log.Printf("--------> Relaying BACK TO RELAYED NODE PKT [ SourceIP: %s ], [ SourceKeyHash: %s ], [ DstIP: %s ], [ DstHashKey: %s ] \n",
source.String(), srcPeerKeyHash, conf.Endpoint.String(), dstPeerKeyHash)
_, err = p.Server.WriteToUDP(buffer[:n+packet.MessageProxySize], conf.Endpoint)
if err != nil {
log.Println("Failed to send to remote: ", err)
}
return
}
}
}
}
if peerInfo, ok := common.PeerKeyHashMap[srcPeerKeyHash]; ok {
log.Printf("PROXING TO LOCAL!!!---> %s <<<< %s <<<<<<<< %s [[ RECV PKT [SRCKEYHASH: %s], [DSTKEYHASH: %s], SourceIP: [%s] ]]\n",
peerInfo.LocalConn.RemoteAddr(), peerInfo.LocalConn.LocalAddr(),
fmt.Sprintf("%s:%d", source.IP.String(), source.Port), srcPeerKeyHash, dstPeerKeyHash, source.IP.String())
_, err = peerInfo.LocalConn.Write(buffer[:n])
if err != nil {
log.Println("Failed to proxy to Wg local interface: ", err)
//continue
}
go func(n int, peerKey string) {
metrics.MetricsMapLock.Lock()
metric := metrics.MetricsMap[peerKey]
metric.TrafficRecieved += float64(n) / (1 << 20)
metric.ConnectionStatus = true
metrics.MetricsMap[peerKey] = metric
metrics.MetricsMapLock.Unlock()
}(n, peerInfo.PeerKey)
return
}
}
// Create - creats a proxy listener
// port - port for proxy to listen on localhost
// bodySize - default 10000, leave 0 to use default
// addr - the address for proxy to listen on
// forwards - indicate address to forward to, {"<address:port>",...} format
func (p *ProxyServer) CreateProxyServer(port, bodySize int, addr string) (err error) {
if p == nil {
p = &ProxyServer{}
}
p.Config.Port = port
p.Config.BodySize = bodySize
p.setDefaults()
p.Server, err = net.ListenUDP("udp", &net.UDPAddr{
Port: p.Config.Port,
IP: net.ParseIP(addr),
})
return
}
func (p *ProxyServer) KeepAlive(ip string, port int) {
for {
_, _ = p.Server.WriteToUDP([]byte("hello-proxy"), &net.UDPAddr{
IP: net.ParseIP(ip),
Port: port,
})
//log.Println("Sending MSg: ", ip, port, err)
time.Sleep(time.Second * 5)
}
}
// Proxy.setDefaults - sets all defaults of proxy listener
func (p *ProxyServer) setDefaults() {
p.setDefaultBodySize()
p.setDefaultPort()
}
// Proxy.setDefaultPort - sets default port of Proxy listener if 0
func (p *ProxyServer) setDefaultPort() {
if p.Config.Port == 0 {
p.Config.Port = defaultPort
}
}
// Proxy.setDefaultBodySize - sets default body size of Proxy listener if 0
func (p *ProxyServer) setDefaultBodySize() {
if p.Config.BodySize == 0 {
p.Config.BodySize = defaultBodySize
}
}

View File

@@ -1,69 +0,0 @@
package stun
import (
"fmt"
"log"
"net"
"strconv"
"strings"
"github.com/gravitl/netmaker/nm-proxy/models"
"gortc.io/stun"
)
type HostInfo struct {
PublicIp net.IP
PrivIp net.IP
PubPort int
PrivPort int
}
var Host HostInfo
func GetHostInfo(stunHostAddr string) (info HostInfo) {
s, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:3478", stunHostAddr))
if err != nil {
log.Println("Resolve: ", err)
return
}
l := &net.UDPAddr{
IP: net.ParseIP(""),
Port: models.NmProxyPort,
}
conn, err := net.DialUDP("udp", l, s)
if err != nil {
log.Println(err)
return
}
defer conn.Close()
c, err := stun.NewClient(conn)
if err != nil {
log.Println(err)
return
}
defer c.Close()
re := strings.Split(conn.LocalAddr().String(), ":")
info.PrivIp = net.ParseIP(re[0])
info.PrivPort, _ = strconv.Atoi(re[1])
// Building binding request with random transaction id.
message := stun.MustBuild(stun.TransactionID, stun.BindingRequest)
// Sending request to STUN server, waiting for response message.
if err := c.Do(message, func(res stun.Event) {
if res.Error != nil {
log.Println("stun error: ", res.Error)
return
}
// Decoding XOR-MAPPED-ADDRESS attribute from message.
var xorAddr stun.XORMappedAddress
if err := xorAddr.GetFrom(res.Message); err != nil {
log.Println("stun error: ", res.Error)
return
}
info.PublicIp = xorAddr.IP
info.PubPort = xorAddr.Port
}); err != nil {
log.Println("stun error: ", err)
}
return
}

View File

@@ -1,321 +0,0 @@
package wg
import (
"errors"
"fmt"
"log"
"net"
"os"
"os/exec"
"strings"
"sync"
"time"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
const (
DefaultMTU = 1280
DefaultWgPort = 51820
DefaultWgKeepAlive = 20 * time.Second
)
// WGIface represents a interface instance
type WGIface struct {
Name string
Port int
MTU int
Device *wgtypes.Device
Address WGAddress
Interface NetInterface
mu sync.Mutex
}
// NetInterface represents a generic network tunnel interface
type NetInterface interface {
Close() error
}
// WGAddress Wireguard parsed address
type WGAddress struct {
IP net.IP
Network *net.IPNet
}
// NewWGIFace Creates a new Wireguard interface instance
func NewWGIFace(iface string, address string, mtu int) (*WGIface, error) {
wgIface := &WGIface{
Name: iface,
MTU: mtu,
mu: sync.Mutex{},
}
wgAddress, err := parseAddress(address)
if err != nil {
return wgIface, err
}
wgIface.Address = wgAddress
err = wgIface.GetWgIface(iface)
if err != nil {
return nil, err
}
return wgIface, nil
}
func (w *WGIface) GetWgIface(iface string) error {
wgClient, err := wgctrl.New()
if err != nil {
return err
}
dev, err := wgClient.Device(iface)
if err != nil {
return err
}
//log.Printf("----> DEVICE: %+v\n", dev)
w.Device = dev
w.Port = dev.ListenPort
return nil
}
func GetWgIfacePubKey(iface string) [32]byte {
wgClient, err := wgctrl.New()
if err != nil {
log.Println("Error fetching pub key: ", iface, err)
return [32]byte{}
}
dev, err := wgClient.Device(iface)
if err != nil {
log.Println("Error fetching pub key: ", iface, err)
return [32]byte{}
}
return dev.PublicKey
}
func GetWgIfacePrivKey(iface string) [32]byte {
wgClient, err := wgctrl.New()
if err != nil {
log.Println("Error fetching pub key: ", iface, err)
return [32]byte{}
}
dev, err := wgClient.Device(iface)
if err != nil {
log.Println("Error fetching pub key: ", iface, err)
return [32]byte{}
}
return dev.PrivateKey
}
// parseAddress parse a string ("1.2.3.4/24") address to WG Address
func parseAddress(address string) (WGAddress, error) {
ip, network, err := net.ParseCIDR(address)
if err != nil {
return WGAddress{}, err
}
return WGAddress{
IP: ip,
Network: network,
}, nil
}
// UpdatePeer updates existing Wireguard Peer or creates a new one if doesn't exist
func (w *WGIface) UpdatePeer(peerKey string, allowedIps []net.IPNet, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
w.mu.Lock()
defer w.mu.Unlock()
log.Printf("updating interface %s peer %s: endpoint %s ", w.Name, peerKey, endpoint)
// //parse allowed ips
// _, ipNet, err := net.ParseCIDR(allowedIps)
// if err != nil {
// return err
// }
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
if err != nil {
return err
}
peer := wgtypes.PeerConfig{
PublicKey: peerKeyParsed,
ReplaceAllowedIPs: true,
AllowedIPs: allowedIps,
PersistentKeepaliveInterval: &keepAlive,
PresharedKey: preSharedKey,
Endpoint: endpoint,
}
config := wgtypes.Config{
Peers: []wgtypes.PeerConfig{peer},
}
err = w.configureDevice(config)
if err != nil {
return fmt.Errorf("received error \"%v\" while updating peer on interface %s with settings: allowed ips %s, endpoint %s", err, w.Name, allowedIps, endpoint.String())
}
return nil
}
// configureDevice configures the wireguard device
func (w *WGIface) configureDevice(config wgtypes.Config) error {
wg, err := wgctrl.New()
if err != nil {
return err
}
defer wg.Close()
// validate if device with name exists
_, err = wg.Device(w.Name)
if err != nil {
return err
}
log.Printf("got Wireguard device %s\n", w.Name)
return wg.ConfigureDevice(w.Name, config)
}
// GetListenPort returns the listening port of the Wireguard endpoint
func (w *WGIface) GetListenPort() (*int, error) {
log.Printf("getting Wireguard listen port of interface %s", w.Name)
//discover Wireguard current configuration
wg, err := wgctrl.New()
if err != nil {
return nil, err
}
defer wg.Close()
d, err := wg.Device(w.Name)
if err != nil {
return nil, err
}
log.Printf("got Wireguard device listen port %s, %d", w.Name, d.ListenPort)
return &d.ListenPort, nil
}
// GetRealIface - retrieves tun iface based on reference iface name from config file
func GetRealIface(iface string) (string, error) {
RunCmd("wg show interfaces", false)
ifacePath := "/var/run/wireguard/" + iface + ".name"
if !(FileExists(ifacePath)) {
return "", errors.New(ifacePath + " does not exist")
}
realIfaceName, err := GetFileAsString(ifacePath)
if err != nil {
return "", err
}
realIfaceName = strings.TrimSpace(realIfaceName)
if !(FileExists(fmt.Sprintf("/var/run/wireguard/%s.sock", realIfaceName))) {
return "", errors.New("interface file does not exist")
}
return realIfaceName, nil
}
// FileExists - checks if file exists locally
func FileExists(f string) bool {
info, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
if err != nil && strings.Contains(err.Error(), "not a directory") {
return false
}
if err != nil {
log.Println(0, "error reading file: "+f+", "+err.Error())
}
return !info.IsDir()
}
// GetFileAsString - returns the string contents of a given file
func GetFileAsString(path string) (string, error) {
content, err := os.ReadFile(path)
if err != nil {
return "", err
}
return string(content), err
}
// RunCmd - runs a local command
func RunCmd(command string, printerr bool) (string, error) {
args := strings.Fields(command)
cmd := exec.Command(args[0], args[1:]...)
cmd.Wait()
out, err := cmd.CombinedOutput()
if err != nil && printerr {
log.Println("error running command: ", command)
log.Println(strings.TrimSuffix(string(out), "\n"))
}
return string(out), err
}
// RemovePeer removes a Wireguard Peer from the interface iface
func (w *WGIface) RemovePeer(peerKey string) error {
w.mu.Lock()
defer w.mu.Unlock()
log.Printf("Removing peer %s from interface %s ", peerKey, w.Name)
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
if err != nil {
return err
}
peer := wgtypes.PeerConfig{
PublicKey: peerKeyParsed,
Remove: true,
}
config := wgtypes.Config{
Peers: []wgtypes.PeerConfig{peer},
}
err = w.configureDevice(config)
if err != nil {
return fmt.Errorf("received error \"%v\" while removing peer %s from interface %s", err, peerKey, w.Name)
}
return nil
}
// UpdatePeer
func (w *WGIface) Update(peerConf wgtypes.PeerConfig, updateOnly bool) error {
w.mu.Lock()
defer w.mu.Unlock()
var err error
log.Printf("---------> NEWWWWWW Updating peer %+v from interface %s ", peerConf, w.Name)
peerConf.UpdateOnly = updateOnly
peerConf.ReplaceAllowedIPs = true
config := wgtypes.Config{
Peers: []wgtypes.PeerConfig{peerConf},
}
err = w.configureDevice(config)
if err != nil {
return fmt.Errorf("received error \"%v\" while Updating peer %s from interface %s", err, peerConf.PublicKey.String(), w.Name)
}
return nil
}
func GetPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
wg, err := wgctrl.New()
if err != nil {
return wgtypes.Peer{}, err
}
defer func() {
err = wg.Close()
if err != nil {
log.Printf("got error while closing wgctl: %v", err)
}
}()
wgDevice, err := wg.Device(ifaceName)
if err != nil {
return wgtypes.Peer{}, err
}
for _, peer := range wgDevice.Peers {
if peer.PublicKey.String() == peerPubKey {
return peer, nil
}
}
return wgtypes.Peer{}, fmt.Errorf("peer not found")
}

509
scripts/nm-upgrade.sh Normal file
View File

@@ -0,0 +1,509 @@
#!/bin/bash
# check_version - make sure current version is 0.17.1 before continuing
check_version() {
IMG_TAG=$(yq -r '.services.netmaker.image' docker-compose.yml)
if [[ "$IMG_TAG" == *"v0.17.1"* ]]; then
echo "version is $IMG_TAG"
else
echo "error, current version is $IMG_TAG"
echo "please upgrade to v0.17.1 in order to use the upgrade script"
exit 1
fi
}
# wait_seconds - wait a number of seconds, print a log
wait_seconds() {
for ((a=1; a <= $1; a++))
do
echo ". . ."
sleep 1
done
}
# confirm - confirm a choice, or exit script
confirm() {
while true; do
read -p 'Does everything look right? [y/n]: ' yn
case $yn in
[Yy]* ) override="true"; break;;
[Nn]* ) echo "exiting..."; exit 1;;
* ) echo "Please answer yes or no.";;
esac
done
}
# install_dependencies - install system dependencies necessary for script to run
install_dependencies() {
OS=$(uname)
is_ubuntu=$(sudo cat /etc/lsb-release | grep "Ubuntu")
if [ "${is_ubuntu}" != "" ]; then
dependencies="yq jq wireguard jq docker.io docker-compose"
update_cmd='apt update'
install_cmd='snap install'
elif [ -f /etc/debian_version ]; then
dependencies="yq jq wireguard jq docker.io docker-compose"
update_cmd='apt update'
install_cmd='apt install -y'
elif [ -f /etc/centos-release ]; then
dependencies="wireguard jq docker.io docker-compose"
update_cmd='yum update'
install_cmd='yum install -y'
elif [ -f /etc/fedora-release ]; then
dependencies="wireguard jq docker.io docker-compose"
update_cmd='dnf update'
install_cmd='dnf install -y'
elif [ -f /etc/redhat-release ]; then
dependencies="wireguard jq docker.io docker-compose"
update_cmd='yum update'
install_cmd='yum install -y'
elif [ -f /etc/arch-release ]; then
dependecies="wireguard-tools jq docker.io docker-compose netclient"
update_cmd='pacman -Sy'
install_cmd='pacman -S --noconfirm'
else
echo "OS not supported for automatic install"
exit 1
fi
set -- $dependencies
${update_cmd}
set +e
while [ -n "$1" ]; do
is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
if [ "${is_installed}" != "" ]; then
echo " " $1 is installed
else
echo " " $1 is not installed. Attempting install.
${install_cmd} $1
sleep 5
if [ "${OS}" = "OpenWRT" ] || [ "${OS}" = "TurrisOS" ]; then
is_installed=$(opkg list-installed $1 | grep $1)
else
is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
fi
if [ "${is_installed}" != "" ]; then
echo " " $1 is installed
elif [ -x "$(command -v $1)" ]; then
echo " " $1 is installed
else
echo " " FAILED TO INSTALL $1
echo " " This may break functionality.
fi
fi
shift
done
set -e
echo "-----------------------------------------------------"
echo "dependency install complete"
echo "-----------------------------------------------------"
}
# collect_server_settings - retrieve server settings from existing compose file
collect_server_settings() {
MASTER_KEY=$(yq -r .services.netmaker.environment.MASTER_KEY docker-compose.yml)
echo "-----------------------------------------------------"
echo "Is $MASTER_KEY the correct master key for your Netmaker installation?"
echo "-----------------------------------------------------"
select mkey_option in "yes" "no (enter manually)"; do
case $REPLY in
1)
echo "using $MASTER_KEY for master key"
break
;;
2)
read -p "Enter Master Key: " mkey
MASTER_KEY=$mkey
echo "using $MASTER_KEY"
break
;;
*) echo "invalid option $REPLY, choose 1 or 2";;
esac
done
SERVER_HTTP_HOST=$(yq -r .services.netmaker.environment.SERVER_HTTP_HOST docker-compose.yml)
echo "-----------------------------------------------------"
echo "Is $SERVER_HTTP_HOST the correct api endpoint for your Netmaker installation?"
echo "-----------------------------------------------------"
select endpoint_option in "yes" "no (enter manually)"; do
case $REPLY in
1)
echo "using $SERVER_HTTP_HOST for api endpoint"
break
;;
2)
read -p "Enter API Endpoint: " endpoint
SERVER_HTTP_HOST=$endpoint
echo "using $SERVER_HTTP_HOST"
break
;;
*) echo "invalid option $REPLY";;
esac
done
BROKER_NAME=$(yq -r .services.netmaker.environment.SERVER_NAME docker-compose.yml)
echo "-----------------------------------------------------"
echo "Is $BROKER_NAME the correct domain for your MQ broker?"
echo "-----------------------------------------------------"
select broker_option in "yes" "no (enter manually)"; do
case $REPLY in
1)
echo "using $BROKER_NAME for endpoint"
break
;;
2)
read -p "Enter Broker Domain: " broker
BROKER_NAME=$broker
echo "using $BROKER_NAME"
break
;;
*) echo "invalid option $REPLY";;
esac
done
SERVER_NAME=${BROKER_NAME#"broker."}
echo "-----------------------------------------------------"
echo "Is $SERVER_NAME the correct base domain for your installation?"
echo "-----------------------------------------------------"
select domain_option in "yes" "no (enter manually)"; do
case $REPLY in
1)
echo "using $SERVER_NAME for domain"
break
;;
2)
read -p "Enter Server Domain: " broker
SERVER_NAME=$server
echo "using $SERVER_NAME"
break
;;
*) echo "invalid option $REPLY";;
esac
done
STUN_NAME="stun.$SERVER_NAME"
echo "-----------------------------------------------------"
echo "Netmaker v0.18.0 requires a new DNS entry for $STUN_NAME."
echo "Please confirm this is added to your DNS provider before continuing"
echo "(note: this is not required if using an nip.io address)"
echo "-----------------------------------------------------"
confirm
}
# collect_node_settings - get existing server node configuration
collect_node_settings() {
curl -s -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://$SERVER_HTTP_HOST/api/nodes | jq -c '[ .[] | select(.isserver=="yes") ]' > nodejson.tmp
NODE_LEN=$(jq length nodejson.tmp)
HAS_INGRESS="no"
if [ "$NODE_LEN" -gt 0 ]; then
echo "===SERVER NODES==="
for i in $(seq 1 $NODE_LEN); do
NUM=$(($i-1))
echo " SERVER NODE $NUM:"
echo " network: $(jq -r ".[$NUM].network" ./nodejson.tmp)"
echo " name: $(jq -r ".[$NUM].name" ./nodejson.tmp)"
echo " private ipv4: $(jq -r ".[$NUM].address" ./nodejson.tmp)"
echo " private ipv6: $(jq -r ".[$NUM].address6" ./nodejson.tmp)"
echo " is egress: $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp)"
if [[ $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp) == "yes" ]]; then
echo " egress range: $(jq -r ".[$NUM].egressgatewayranges" ./nodejson.tmp)"
fi
echo " is ingress: $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp)"
if [[ $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp) == "yes" ]]; then
HAS_INGRESS="yes"
fi
echo " is relay: $(jq -r ".[$NUM].isrelay" ./nodejson.tmp)"
if [[ $(jq -r ".[$NUM].isrelay" ./nodejson.tmp) == "yes" ]]; then
HAS_RELAY="yes"
echo " relay addrs: $(jq -r ".[$NUM].relayaddrs" ./nodejson.tmp | tr -d '[]\n"[:space:]')"
fi
echo " is failover: $(jq -r ".[$NUM].failover" ./nodejson.tmp)"
echo " ------------"
done
echo "=================="
else
echo "no nodes to parse"
fi
echo "Please confirm that the above output matches the server nodes in your Netmaker server."
confirm
if [[ $HAS_INGRESS == "yes" ]]; then
echo "WARNING: Your server contains an Ingress Gateway. After upgrading, existing Ext Clients will be lost and must be recreated. Please confirm that you would like to continue."
confirm
fi
}
# set_compose - set compose file with proper values
set_compose() {
# DEV_TEMP - Temporary instructions for testing
sed -i "s/v0.17.1/testing/g" /root/docker-compose.yml
# RELEASE_REPLACE - Use this once release is ready
#sed -i "s/v0.17.1/v0.18.0/g" /root/docker-compose.yml
yq ".services.netmaker.environment.SERVER_NAME = \"$SERVER_NAME\"" -i /root/docker-compose.yml
yq ".services.netmaker.environment += {\"BROKER_NAME\": \"$BROKER_NAME\"}" -i /root/docker-compose.yml
yq ".services.netmaker.environment += {\"STUN_NAME\": \"$STUN_NAME\"}" -i /root/docker-compose.yml
yq ".services.netmaker.environment += {\"STUN_PORT\": \"3478\"}" -i /root/docker-compose.yml
yq ".services.netmaker.ports += \"3478:3478/udp\"" -i /root/docker-compose.yml
}
# start_containers - run docker-compose up -d
start_containers() {
docker-compose -f /root/docker-compose.yml up -d
}
# test_caddy - make sure caddy is working
test_caddy() {
echo "Testing Caddy setup (please be patient, this may take 1-2 minutes)"
for i in 1 2 3 4 5 6 7 8
do
curlresponse=$(curl -vIs https://${SERVER_HTTP_HOST} 2>&1)
if [[ "$i" == 8 ]]; then
echo " Caddy is having an issue setting up certificates, please investigate (docker logs caddy)"
echo " Exiting..."
exit 1
elif [[ "$curlresponse" == *"failed to verify the legitimacy of the server"* ]]; then
echo " Certificates not yet configured, retrying..."
elif [[ "$curlresponse" == *"left intact"* ]]; then
echo " Certificates ok"
break
else
secs=$(($i*5+10))
echo " Issue establishing connection...retrying in $secs seconds..."
fi
sleep $secs
done
}
# setup_netclient - installs netclient locally
setup_netclient() {
# DEV_TEMP - Temporary instructions for testing
wget https://fileserver.netmaker.org/testing/netclient
chmod +x netclient
./netclient install
# RELEASE_REPLACE - Use this once release is ready
# if [ -f /etc/debian_version ]; then
# curl -sL 'https://apt.netmaker.org/gpg.key' | sudo tee /etc/apt/trusted.gpg.d/netclient.asc
# curl -sL 'https://apt.netmaker.org/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/netclient.list
# sudo apt update
# sudo apt install netclient
# elif [ -f /etc/centos-release ]; then
# curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
# curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
# sudo rpm --import /tmp/gpg.key
# sudo dnf check-update
# sudo dnf install netclient
# elif [ -f /etc/fedora-release ]; then
# curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
# curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
# sudo rpm --import /tmp/gpg.key
# sudo dnf check-update
# sudo dnf install netclient
# elif [ -f /etc/redhat-release ]; then
# curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
# curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
# sudo rpm --import /tmp/gpg.key
# sudo dnf check-update(
# sudo dnf install netclient
# elif [ -f /etc/arch-release ]; then
# yay -S netclient
# else
# echo "OS not supported for automatic install"
# exit 1
# fi
# if [ -z "${install_cmd}" ]; then
# echo "OS unsupported for automatic dependency install"
# exit 1
# fi
}
# setup_nmctl - pulls nmctl and makes it executable
setup_nmctl() {
# DEV_TEMP - Temporary instructions for testing
wget https://fileserver.netmaker.org/testing/nmctl
# RELEASE_REPLACE - Use this once release is ready
# wget https://github.com/gravitl/netmaker/releases/download/v0.17.1/nmctl
chmod +x nmctl
echo "using server $SERVER_HTTP_HOST"
echo "using master key $MASTER_KEY"
./nmctl context set default --endpoint="https://$SERVER_HTTP_HOST" --master_key="$MASTER_KEY"
./nmctl context use default
RESP=$(./nmctl network list)
if [[ $RESP == *"unauthorized"* ]]; then
echo "Unable to properly configure NMCTL, exiting..."
exit 1
fi
}
# join_networks - joins netclient into the networks using old settings
join_networks() {
NODE_LEN=$(jq length nodejson.tmp)
HAS_INGRESS="no"
if [ "$NODE_LEN" -gt 0 ]; then
for i in $(seq 1 $NODE_LEN); do
NUM=$(($i-1))
NETWORK=$(jq -r ".[$NUM].network" ./nodejson.tmp)
echo " joining network $NETWORK with following settings. Please confirm:"
echo " network: $(jq -r ".[$NUM].network" ./nodejson.tmp)"
echo " name: $(jq -r ".[$NUM].name" ./nodejson.tmp)"
echo " private ipv4: $(jq -r ".[$NUM].address" ./nodejson.tmp)"
echo " private ipv6: $(jq -r ".[$NUM].address6" ./nodejson.tmp)"
echo " is egress: $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp)"
if [[ $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp) == "yes" ]]; then
HAS_EGRESS="yes"
echo " egress range: $(jq -r ".[$NUM].egressgatewayranges" ./nodejson.tmp)"
fi
echo " is ingress: $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp)"
if [[ $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp) == "yes" ]]; then
HAS_INGRESS="yes"
fi
echo " is relay: $(jq -r ".[$NUM].isrelay" ./nodejson.tmp)"
if [[ $(jq -r ".[$NUM].isrelay" ./nodejson.tmp) == "yes" ]]; then
HAS_RELAY="yes"
RELAY_ADDRS=$(jq -r ".[$NUM].relayaddrs" ./nodejson.tmp | tr -d '[]\n"[:space:]')
fi
echo " is failover: $(jq -r ".[$NUM].failover" ./nodejson.tmp)"
if [[ $(jq -r ".[$NUM].failover" ./nodejson.tmp) == "yes" ]]; then
HAS_FAILOVER="yes"
fi
echo " ------------"
confirm
echo "running command: ./nmctl keys create $NETWORK 1"
KEY_JSON=$(./nmctl keys create $NETWORK 1)
KEY=$(echo $KEY_JSON | jq -r .accessstring)
echo "join key created: $KEY"
NAME=$(jq -r ".[$NUM].name" ./nodejson.tmp)
ADDRESS=$(jq -r ".[$NUM].address" ./nodejson.tmp)
ADDRESS6=$(jq -r ".[$NUM].address6" ./nodejson.tmp)
if [[ ! -z "$ADDRESS6" ]]; then
echo "joining with command: netclient join -t $KEY --name=$NAME --address=$ADDRESS --address6=$ADDRESS6
"
confirm
netclient join -t $KEY --name=$NAME --address=$ADDRESS --address6=$ADDRESS6
else
echo "joining with command: netclient join -t $KEY --name=$NAME --address=$ADDRESS"
confirm
netclient join -t $KEY --name=$NAME --address=$ADDRESS
fi
NODE_ID=$(sudo cat /etc/netclient/nodes.yml | yq -r .$NETWORK.commonnode.id)
echo "join complete. New node ID: $NODE_ID"
if [[ $NUM -eq 0 ]]; then
HOST_ID=$(sudo cat /etc/netclient/netclient.yml | yq -r .host.id)
echo "For first join, making host a default"
echo "Host ID: $HOST_ID"
# set as a default host
# TODO - this command is not working
./nmctl host update $HOST_ID --default
fi
# create an egress if necessary
if [[ $HAS_EGRESS == "yes" ]]; then
echo "Egress is currently unimplemented. Wait for 0.18.1"
fi
echo "HAS INGRESS: $HAS_INGRESS"
# create an ingress if necessary
if [[ $HAS_INGRESS == "yes" ]]; then
if [[ $HAS_FAILOVER == "yes" ]]; then
echo "creating ingress and failover..."
./nmctl node create_ingress $NETWORK $NODE_ID --failover
else
echo "creating ingress..."
./nmctl node create_ingress $NETWORK $NODE_ID
fi
fi
# relay
if [[ $HAS_RELAY == "yes" ]]; then
echo "creating relay..."
./nmctl node create_relay $NETWORK $NODE_ID $RELAY_ADDRS
fi
done
echo "=================="
else
echo "no networks to join"
fi
}
cat << "EOF"
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The Netmaker Upgrade Script: Upgrading to v0.18.0 so you don't have to!
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EOF
set -e
if [ $(id -u) -ne 0 ]; then
echo "This script must be run as root"
exit 1
fi
echo "...installing dependencies for script"
install_dependencies
echo "...confirming version is correct"
check_version
echo "...collecting necessary server settings"
collect_server_settings
echo "...setup nmctl"
setup_nmctl
echo "...retrieving current server node settings"
collect_node_settings
echo "...backing up docker compose to docker-compose.yml.backup"
cp /root/docker-compose.yml /root/docker-compose.yml.backup
echo "...setting docker-compose values"
set_compose
echo "...starting containers"
start_containers
wait_seconds 3
echo "..testing Caddy proxy"
test_caddy
echo "..testing Netmaker health"
# TODO, implement health check
# netmaker_health_check
# wait_seconds 2
echo "...setting up netclient (this may take a minute, be patient)"
setup_netclient
wait_seconds 2
echo "...join networks"
join_networks
echo "-----------------------------------------------------------------"
echo "-----------------------------------------------------------------"
echo "Netmaker setup is now complete. You are ready to begin using Netmaker."
echo "Visit dashboard.$SERVER_NAME to log in"
echo "-----------------------------------------------------------------"
echo "-----------------------------------------------------------------"

View File

@@ -76,15 +76,7 @@ func GetServerConfig() config.ServerConfig {
cfg.ClientID = authInfo[1]
cfg.ClientSecret = authInfo[2]
cfg.FrontendURL = GetFrontendURL()
if GetRce() {
cfg.RCE = "on"
} else {
cfg.RCE = "off"
}
cfg.Telemetry = Telemetry()
cfg.ManageIPTables = ManageIPTables()
services := strings.Join(GetPortForwardServiceList(), ",")
cfg.PortForwardServices = services
cfg.Server = GetServer()
cfg.Verbosity = GetVerbosity()
cfg.IsEE = "no"
@@ -379,18 +371,6 @@ func Telemetry() string {
return telemetry
}
// ManageIPTables - checks if iptables should be manipulated on host
func ManageIPTables() string {
manage := "on"
if os.Getenv("MANAGE_IPTABLES") == "off" {
manage = "off"
}
if config.Config.Server.ManageIPTables == "off" {
manage = "off"
}
return manage
}
// GetServer - gets the server name
func GetServer() string {
server := ""
@@ -528,19 +508,6 @@ func GetPlatform() string {
return platform
}
// GetIPForwardServiceList - get the list of services that the server should be forwarding
func GetPortForwardServiceList() []string {
//services := "mq,dns,ssh"
services := ""
if os.Getenv("PORT_FORWARD_SERVICES") != "" {
services = os.Getenv("PORT_FORWARD_SERVICES")
} else if config.Config.Server.PortForwardServices != "" {
services = config.Config.Server.PortForwardServices
}
serviceSlice := strings.Split(services, ",")
return serviceSlice
}
// GetSQLConn - get the sql connection string
func GetSQLConn() string {
sqlconn := "http://"
@@ -552,17 +519,6 @@ func GetSQLConn() string {
return sqlconn
}
// IsHostNetwork - checks if running on host network
func IsHostNetwork() bool {
ishost := false
if os.Getenv("HOST_NETWORK") == "on" {
ishost = true
} else if config.Config.Server.HostNetwork == "on" {
ishost = true
}
return ishost
}
// GetNodeID - gets the node id
func GetNodeID() string {
var id string
@@ -640,11 +596,6 @@ func GetAzureTenant() string {
return azureTenant
}
// GetRce - sees if Rce is enabled, off by default
func GetRce() bool {
return os.Getenv("RCE") == "on" || config.Config.Server.RCE == "on"
}
// GetMQServerPort - get mq port for server
func GetMQServerPort() string {
port := "1883" //default

View File

@@ -1,136 +0,0 @@
package serverctl
import (
"errors"
"net"
"os"
"os/exec"
"strings"
"time"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/servercfg"
)
const netmakerProcessName = "netmaker"
// InitIPTables - intializes the server iptables
func InitIPTables(force bool) error {
_, err := exec.LookPath("iptables")
if err != nil {
return err
}
err = setForwardPolicy()
if err != nil {
logger.Log(0, "error setting iptables forward policy: "+err.Error())
}
err = portForwardServices(force)
if err != nil {
return err
}
if isContainerized() && servercfg.IsHostNetwork() {
err = setHostCoreDNSMapping()
}
return err
}
// set up port forwarding for services listed in config
func portForwardServices(force bool) error {
var err error
services := servercfg.GetPortForwardServiceList()
if len(services) == 0 || services[0] == "" {
return nil
}
for _, service := range services {
switch service {
case "mq":
err = iptablesPortForward("mq", servercfg.GetMQServerPort(), servercfg.GetMQServerPort(), false, force)
case "dns":
err = iptablesPortForward("coredns", "53", "53", false, force)
case "ssh":
err = iptablesPortForward("netmaker", "22", "22", false, force)
default:
params := strings.Split(service, ":")
if len(params) == 3 {
err = iptablesPortForward(params[0], params[1], params[2], true, force)
}
}
if err != nil {
return err
}
}
return nil
}
// determine if process is running in container
func isContainerized() bool {
fileBytes, err := os.ReadFile("/proc/1/sched")
if err != nil {
logger.Log(1, "error determining containerization: "+err.Error())
return false
}
fileString := string(fileBytes)
return strings.Contains(fileString, netmakerProcessName)
}
// make sure host allows forwarding
func setForwardPolicy() error {
logger.Log(2, "setting iptables forward policy")
_, err := ncutils.RunCmd("iptables --policy FORWARD ACCEPT", false)
return err
}
// port forward from an entry, can contain a dns name for lookup
func iptablesPortForward(entry string, inport string, outport string, isIP, force bool) error {
var address string
if !isIP {
out:
for i := 1; i < 4; i++ {
ips, err := net.LookupIP(entry)
if err != nil && i > 2 {
return err
}
for _, ip := range ips {
if ipv4 := ip.To4(); ipv4 != nil {
address = ipv4.String()
}
}
if address != "" {
break out
}
time.Sleep(time.Second)
}
} else {
address = entry
}
if address == "" {
return errors.New("could not locate ip for " + entry)
}
if output, err := ncutils.RunCmd("iptables -t nat -C PREROUTING -p tcp --dport "+inport+" -j DNAT --to-destination "+address+":"+outport, false); output != "" || err != nil || force {
_, err := ncutils.RunCmd("iptables -t nat -A PREROUTING -p tcp --dport "+inport+" -j DNAT --to-destination "+address+":"+outport, false)
if err != nil {
return err
}
_, err = ncutils.RunCmd("iptables -t nat -A PREROUTING -p udp --dport "+inport+" -j DNAT --to-destination "+address+":"+outport, false)
if err != nil {
return err
}
_, err = ncutils.RunCmd("iptables -t nat -A POSTROUTING -j MASQUERADE", false)
return err
} else {
logger.Log(3, "mq forwarding is already set... skipping")
}
return nil
}
// if running in host networking mode, run iptables to map to CoreDNS container
func setHostCoreDNSMapping() error {
logger.Log(1, "forwarding dns traffic on host from netmaker interfaces to 53053")
ncutils.RunCmd("iptables -t nat -A PREROUTING -i nm-+ -p tcp --match tcp --dport 53 --jump REDIRECT --to-ports 53053", true)
_, err := ncutils.RunCmd("iptables -t nat -A PREROUTING -i nm-+ -p udp --match udp --dport 53 --jump REDIRECT --to-ports 53053", true)
return err
}

View File

@@ -85,7 +85,7 @@ func setNetworkDefaults() error {
}
} else {
network.SetDefaults()
_, _, _, _, _, _, err = logic.UpdateNetwork(&network, &network)
_, _, _, _, _, err = logic.UpdateNetwork(&network, &network)
if err != nil {
logger.Log(0, "could not set defaults on network", network.NetID)
}

View File

@@ -105,12 +105,6 @@ definitions:
nodeid:
type: string
x-go-name: NodeID
postdown:
type: string
x-go-name: PostDown
postup:
type: string
x-go-name: PostUp
ranges:
items:
type: string
@@ -270,12 +264,6 @@ definitions:
format: int32
type: integer
x-go-name: DefaultMTU
defaultpostdown:
type: string
x-go-name: DefaultPostDown
defaultpostup:
type: string
x-go-name: DefaultPostUp
defaultudpholepunch:
type: string
x-go-name: DefaultUDPHolePunch
@@ -291,9 +279,6 @@ definitions:
ispointtosite:
type: string
x-go-name: IsPointToSite
localrange:
type: string
x-go-name: LocalRange
netid:
type: string
x-go-name: NetID
@@ -432,9 +417,6 @@ definitions:
format: int32
type: integer
x-go-name: LocalListenPort
localrange:
type: string
x-go-name: LocalRange
macaddress:
type: string
x-go-name: MacAddress
@@ -460,12 +442,6 @@ definitions:
format: int32
type: integer
x-go-name: PersistentKeepalive
postdown:
type: string
x-go-name: PostDown
postup:
type: string
x-go-name: PostUp
publickey:
type: string
x-go-name: PublicKey
@@ -644,8 +620,6 @@ definitions:
type: string
PublicIPService:
type: string
RCE:
type: string
RestBackend:
type: string
SQLConn:

View File

@@ -467,17 +467,14 @@ func TestUpdateNetwork(t *testing.T) {
})
t.Run("UpdatePostUP", func(t *testing.T) {
type Network struct {
DefaultPostUp string
}
var network Network
network.DefaultPostUp = "sudo wg add-conf wc-netmaker /etc/wireguard/peers/conf"
response, err := api(t, network, http.MethodPut, baseURL+"/api/networks/skynet", "secretkey")
assert.Nil(t, err, err)
assert.Equal(t, http.StatusOK, response.StatusCode)
defer response.Body.Close()
err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
assert.Nil(t, err, err)
assert.Equal(t, network.DefaultPostUp, returnedNetwork.DefaultPostUp)
})
t.Run("UpdatePostDown", func(t *testing.T) {
type Network struct {