Merge branch 'develop' into remove-stun

This commit is contained in:
Alex Feiszli
2023-06-28 13:53:46 -04:00
committed by GitHub
58 changed files with 1080 additions and 880 deletions

View File

@@ -31,6 +31,7 @@ body:
label: Version label: Version
description: What version are you running? description: What version are you running?
options: options:
- v0.20.3
- v0.20.2 - v0.20.2
- v0.20.1 - v0.20.1
- v0.20.0 - v0.20.0

View File

@@ -10,12 +10,13 @@ jobs:
skip-check: skip-check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
skip: ${{ steps.check.outputs.skip }} skip: ${{ steps.skip.outputs.skip }}
steps: steps:
- id: skip - id: skip
uses: fkirc/skip-duplicate-actions@v5 uses: fkirc/skip-duplicate-actions@v5
with: with:
concurrent_skipping: 'always' concurrent_skipping: 'always'
getbranch: getbranch:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: skip-check needs: skip-check
@@ -29,7 +30,7 @@ jobs:
repository: gravitl/netclient repository: gravitl/netclient
ref: develop ref: develop
- name: check if branch exists - name: check if branch exists
id: checkbranch id: getbranch
run: | run: |
if git show-ref ${{ github.head_ref}}; then if git show-ref ${{ github.head_ref}}; then
echo branch exists echo branch exists
@@ -39,12 +40,62 @@ jobs:
echo "netclientbranch=develop" >> $GITHUB_OUTPUT echo "netclientbranch=develop" >> $GITHUB_OUTPUT
fi fi
getserver:
runs-on: ubuntu-latest
needs: skip-check
if: ${{ needs.skip-check.outputs.skip != 'true' }}
outputs:
netmakerserver: ${{ steps.getserver.outputs.server }}
steps:
- name: setup ssh
run: |
mkdir -p ~/.ssh/
echo "$SSH_KEY" > ~/.ssh/id_devops
chmod 600 ~/.ssh/id_devops
cat >>~/.ssh/config <<END
Host *.clustercat.com
User root
IdentityFile ~/.ssh/id_devops
StrictHostKeyChecking no
END
env:
SSH_KEY: ${{ secrets.TESTING_SSH_KEY }}
- name: getserver
id: getserver
run: |
server=""
for arg in "branch1" "branch2" "branch3" "branch4" "branch5"; do
echo checking $arg
result=$( ssh root@server.${arg}.clustercat.com '~/branchtesting/check.sh')
echo $result
if [ "$result" == "pass" ]
then
server=$arg
echo $server >> /tmp/server
break
fi
done
echo server is $server
if [ "$server" == "" ]
then
echo server not set
exit 1
fi
echo "netmakerserver=${ server }" >> $GITHUB_OUTPUT
- name: save server name
uses: actions/upload-artifact@v3
with:
name: server
path: /tmp/ping
retention-days: 3
terraform: terraform:
needs: getbranch needs: [getbranch, getserver]
uses: gravitl/devops/.github/workflows/terraform.yml@master uses: gravitl/devops/.github/workflows/terraform.yml@master
with: with:
netmakerbranch: ${{ github.head_ref }} netmakerbranch: ${{ github.head_ref }}
netclientbranch: ${{ needs.getbranch.outputs.netclientbranch }} netclientbranch: ${{ needs.getbranch.outputs.netclientbranch }}
server: ${{ needs.getserver.outputs.netmakerserver }}
secrets: inherit secrets: inherit

View File

@@ -16,6 +16,9 @@ jobs:
with: with:
run_id: ${{ github.event.workflow_run.id}} run_id: ${{ github.event.workflow_run.id}}
if_no_artifact_found: warn if_no_artifact_found: warn
- name: get server name
run: |
echo "SERVER=$(cat ./server/server) >> $GITHUB_ENV"
- name: discord success message - name: discord success message
uses: appleboy/discord-action@master uses: appleboy/discord-action@master
with: with:
@@ -23,7 +26,7 @@ jobs:
webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
color: "#42f545" color: "#42f545"
username: "GitHub Bot" username: "GitHub Bot"
message: "${{ github.repository }}: ${{ github.event.workflow_run.name }} was successful: droplets from this workflow (tag ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}) will be deleted in 15 min" message: "${{ github.repository }}: ${{ github.event.workflow_run.name }} on dashboard.${{ env.SERVER }}.clustercat.com was successful: droplets from this workflow (tag ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}) will be deleted in 15 min"
file: ./results/results.log file: ./results/results.log
- name: delete droplets - name: delete droplets
if: success() || failure() if: success() || failure()
@@ -36,6 +39,14 @@ jobs:
env: env:
DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }} DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
TAG: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }} TAG: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
- name: mark server as available
uses: appleboy/ssh-action@master
with:
host: server.${{ env.SERVER }}.clustercat.com
username: root
key: ${{ secrets.TESTING_SSH_KEY }}
script: |
rm /tmp/branchtest
on-failure: on-failure:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -46,6 +57,9 @@ jobs:
with: with:
run_id: ${{ github.event.workflow_run.id}} run_id: ${{ github.event.workflow_run.id}}
if_no_artifact_found: warn if_no_artifact_found: warn
- name: get server name
run: |
echo "SERVER=$(cat ./server/server) >> $GITHUB_ENV"
- name: discord failure message - name: discord failure message
uses: appleboy/discord-action@master uses: appleboy/discord-action@master
with: with:
@@ -53,7 +67,7 @@ jobs:
webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
color: "#990000" color: "#990000"
username: "GitHub Bot" username: "GitHub Bot"
message: "${{ github.repository }}: ${{ github.event.workflow_run.name }} failed: droplets from this workflow (tag ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt}}) will be deleted in 5 hours" message: "${{ github.repository }}: ${{ github.event.workflow_run.name }} failed: droplets from this workflow (tag ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt}}) will be deleted in 3 hours"
file: ./results/results.log file: ./results/results.log
- name: discord error message - name: discord error message
uses: appleboy/discord-action@master uses: appleboy/discord-action@master
@@ -67,7 +81,7 @@ jobs:
- name: delete droplets - name: delete droplets
if: success() || failure() if: success() || failure()
run: | run: |
sleep 5h sleep 3h
curl -X GET \ curl -X GET \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \ -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
@@ -75,3 +89,11 @@ jobs:
env: env:
DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }} DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }}
TAG: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }} TAG: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}
- name: mark server as available
uses: appleboy/ssh-action@master
with:
host: server.${{ env.SERVER }}.clustercat.com
username: root
key: ${{ secrets.TESTING_SSH_KEY }}
script: |
rm /tmp/branchtest

View File

@@ -6,7 +6,7 @@ COPY . .
RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} . RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} .
# RUN go build -tags=ee . -o netmaker main.go # RUN go build -tags=ee . -o netmaker main.go
FROM alpine:3.18.0 FROM alpine:3.18.2
# add a c lib # add a c lib
# set the working directory # set the working directory

View File

@@ -1,5 +1,5 @@
#first stage - builder #first stage - builder
FROM alpine:3.18.0 FROM alpine:3.18.2
ARG version ARG version
WORKDIR /app WORKDIR /app
COPY ./netmaker /root/netmaker COPY ./netmaker /root/netmaker

View File

@@ -16,7 +16,7 @@
<p align="center"> <p align="center">
<a href="https://github.com/gravitl/netmaker/releases"> <a href="https://github.com/gravitl/netmaker/releases">
<img src="https://img.shields.io/badge/Version-0.20.2-informational?style=flat-square" /> <img src="https://img.shields.io/badge/Version-0.20.3-informational?style=flat-square" />
</a> </a>
<a href="https://hub.docker.com/r/gravitl/netmaker/tags"> <a href="https://hub.docker.com/r/gravitl/netmaker/tags">
<img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" /> <img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />

View File

@@ -1,22 +0,0 @@
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,22 @@
package node
import (
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var hostCreateRelayCmd = &cobra.Command{
Use: "create_relay [NETWORK][NODE ID] [RELAYED NODE IDS (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(hostCreateRelayCmd)
}

View File

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

View File

@@ -12,7 +12,7 @@ var (
postUp string postUp string
postDown string postDown string
keepAlive int keepAlive int
relayAddrs string relayedNodes string
egressGatewayRanges string egressGatewayRanges string
expirationDateTime int expirationDateTime int
defaultACL bool defaultACL bool

View File

@@ -35,8 +35,8 @@ var nodeUpdateCmd = &cobra.Command{
node.Address6 = address6 node.Address6 = address6
node.LocalAddress = localAddress node.LocalAddress = localAddress
node.PersistentKeepalive = int32(keepAlive) node.PersistentKeepalive = int32(keepAlive)
if relayAddrs != "" { if relayedNodes != "" {
node.RelayAddrs = strings.Split(relayAddrs, ",") node.RelayedNodes = strings.Split(relayedNodes, ",")
} }
if egressGatewayRanges != "" { if egressGatewayRanges != "" {
node.EgressGatewayRanges = strings.Split(egressGatewayRanges, ",") node.EgressGatewayRanges = strings.Split(egressGatewayRanges, ",")
@@ -62,7 +62,7 @@ func init() {
nodeUpdateCmd.Flags().StringVar(&postUp, "post_up", "", "Commands to run after node is up `;` separated") nodeUpdateCmd.Flags().StringVar(&postUp, "post_up", "", "Commands to run after node is up `;` separated")
nodeUpdateCmd.Flags().StringVar(&postDown, "post_down", "", "Commands to run after node is down `;` separated") nodeUpdateCmd.Flags().StringVar(&postDown, "post_down", "", "Commands to run after node is down `;` separated")
nodeUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval in which packets are sent to keep connections open with peers") nodeUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval in which packets are sent to keep connections open with peers")
nodeUpdateCmd.Flags().StringVar(&relayAddrs, "relay_addrs", "", "Addresses for relaying connections if node acts as a relay") nodeUpdateCmd.Flags().StringVar(&relayedNodes, "relayed_nodes", "", "relayed nodes if node acts as a relay")
nodeUpdateCmd.Flags().StringVar(&egressGatewayRanges, "egress_addrs", "", "Addresses for egressing traffic if node acts as an egress") nodeUpdateCmd.Flags().StringVar(&egressGatewayRanges, "egress_addrs", "", "Addresses for egressing traffic if node acts as an egress")
nodeUpdateCmd.Flags().IntVar(&expirationDateTime, "expiry", 0, "UNIX timestamp after which node will lose access to the network") nodeUpdateCmd.Flags().IntVar(&expirationDateTime, "expiry", 0, "UNIX timestamp after which node will lose access to the network")
nodeUpdateCmd.Flags().BoolVar(&defaultACL, "acl", false, "Enable default ACL ?") nodeUpdateCmd.Flags().BoolVar(&defaultACL, "acl", false, "Enable default ACL ?")

View File

@@ -36,17 +36,18 @@ func DeleteHostFromNetwork(hostID, network string) *hostNetworksUpdatePayload {
return request[hostNetworksUpdatePayload](http.MethodDelete, "/api/hosts/"+hostID+"/networks/"+network, nil) return request[hostNetworksUpdatePayload](http.MethodDelete, "/api/hosts/"+hostID+"/networks/"+network, nil)
} }
// CreateRelay - turn a host into a relay // CreateRelay - add relay to a node
func CreateRelay(hostID string, relayedHosts []string) *models.ApiHost { func CreateRelay(netID, nodeID string, relayedNodes []string) *models.ApiNode {
return request[models.ApiHost](http.MethodPost, fmt.Sprintf("/api/hosts/%s/relay", hostID), &models.HostRelayRequest{ return request[models.ApiNode](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/createrelay", netID, nodeID), &models.RelayRequest{
HostID: hostID, NodeID: nodeID,
RelayedHosts: relayedHosts, NetID: netID,
RelayedNodes: relayedNodes,
}) })
} }
// DeleteRelay - remove relay role from a host // DeleteRelay - remove relay from a node
func DeleteRelay(hostID string) *models.ApiHost { func DeleteRelay(netID, nodeID string) *models.ApiNode {
return request[models.ApiHost](http.MethodDelete, fmt.Sprintf("/api/hosts/%s/relay", hostID), nil) return request[models.ApiNode](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deleterelay", netID, nodeID), nil)
} }
// RefreshKeys - refresh wireguard keys // RefreshKeys - refresh wireguard keys

View File

@@ -3,7 +3,7 @@ version: "3.4"
services: services:
netclient: netclient:
container_name: netclient container_name: netclient
image: 'gravitl/netclient:v0.20.2' image: 'gravitl/netclient:v0.20.3'
hostname: netmaker-1 hostname: netmaker-1
network_mode: host network_mode: host
restart: on-failure restart: on-failure

View File

@@ -32,56 +32,62 @@ type EnvironmentConfig struct {
// ServerConfig - server conf struct // ServerConfig - server conf struct
type ServerConfig struct { type ServerConfig struct {
CoreDNSAddr string `yaml:"corednsaddr"` CoreDNSAddr string `yaml:"corednsaddr"`
APIConnString string `yaml:"apiconn"` APIConnString string `yaml:"apiconn"`
APIHost string `yaml:"apihost"` APIHost string `yaml:"apihost"`
APIPort string `yaml:"apiport"` APIPort string `yaml:"apiport"`
Broker string `yam:"broker"` Broker string `yam:"broker"`
ServerBrokerEndpoint string `yaml:"serverbrokerendpoint"` ServerBrokerEndpoint string `yaml:"serverbrokerendpoint"`
BrokerType string `yaml:"brokertype"` BrokerType string `yaml:"brokertype"`
EmqxRestEndpoint string `yaml:"emqxrestendpoint"` EmqxRestEndpoint string `yaml:"emqxrestendpoint"`
NetclientAutoUpdate string `yaml:"netclientautoupdate"` NetclientAutoUpdate string `yaml:"netclientautoupdate"`
MasterKey string `yaml:"masterkey"` NetclientEndpointDetection string `yaml:"netclientendpointdetection"`
DNSKey string `yaml:"dnskey"` MasterKey string `yaml:"masterkey"`
AllowedOrigin string `yaml:"allowedorigin"` DNSKey string `yaml:"dnskey"`
NodeID string `yaml:"nodeid"` AllowedOrigin string `yaml:"allowedorigin"`
RestBackend string `yaml:"restbackend"` NodeID string `yaml:"nodeid"`
MessageQueueBackend string `yaml:"messagequeuebackend"` RestBackend string `yaml:"restbackend"`
DNSMode string `yaml:"dnsmode"` MessageQueueBackend string `yaml:"messagequeuebackend"`
DisableRemoteIPCheck string `yaml:"disableremoteipcheck"` DNSMode string `yaml:"dnsmode"`
Version string `yaml:"version"` DisableRemoteIPCheck string `yaml:"disableremoteipcheck"`
SQLConn string `yaml:"sqlconn"` Version string `yaml:"version"`
Platform string `yaml:"platform"` SQLConn string `yaml:"sqlconn"`
Database string `yaml:"database"` Platform string `yaml:"platform"`
Verbosity int32 `yaml:"verbosity"` Database string `yaml:"database"`
AuthProvider string `yaml:"authprovider"` Verbosity int32 `yaml:"verbosity"`
OIDCIssuer string `yaml:"oidcissuer"` AuthProvider string `yaml:"authprovider"`
ClientID string `yaml:"clientid"` OIDCIssuer string `yaml:"oidcissuer"`
ClientSecret string `yaml:"clientsecret"` ClientID string `yaml:"clientid"`
FrontendURL string `yaml:"frontendurl"` ClientSecret string `yaml:"clientsecret"`
DisplayKeys string `yaml:"displaykeys"` FrontendURL string `yaml:"frontendurl"`
AzureTenant string `yaml:"azuretenant"` DisplayKeys string `yaml:"displaykeys"`
Telemetry string `yaml:"telemetry"` AzureTenant string `yaml:"azuretenant"`
HostNetwork string `yaml:"hostnetwork"` Telemetry string `yaml:"telemetry"`
Server string `yaml:"server"` HostNetwork string `yaml:"hostnetwork"`
PublicIPService string `yaml:"publicipservice"` Server string `yaml:"server"`
MQPassword string `yaml:"mqpassword"` PublicIPService string `yaml:"publicipservice"`
MQUserName string `yaml:"mqusername"` MQPassword string `yaml:"mqpassword"`
MetricsExporter string `yaml:"metrics_exporter"` MQUserName string `yaml:"mqusername"`
BasicAuth string `yaml:"basic_auth"` MetricsExporter string `yaml:"metrics_exporter"`
LicenseValue string `yaml:"license_value"` BasicAuth string `yaml:"basic_auth"`
NetmakerAccountID string `yaml:"netmaker_account_id"` LicenseValue string `yaml:"license_value"`
IsEE string `yaml:"is_ee"` NetmakerAccountID string `yaml:"netmaker_account_id"`
StunPort int `yaml:"stun_port"` IsEE string `yaml:"is_ee"`
StunList string `yaml:"stun_list"` StunPort int `yaml:"stun_port"`
Proxy string `yaml:"proxy"` StunList string `yaml:"stun_list"`
DefaultProxyMode ProxyMode `yaml:"defaultproxymode"` Proxy string `yaml:"proxy"`
TurnServer string `yaml:"turn_server"` DefaultProxyMode ProxyMode `yaml:"defaultproxymode"`
TurnApiServer string `yaml:"turn_api_server"` TurnServer string `yaml:"turn_server"`
TurnPort int `yaml:"turn_port"` TurnApiServer string `yaml:"turn_api_server"`
TurnUserName string `yaml:"turn_username"` TurnPort int `yaml:"turn_port"`
TurnPassword string `yaml:"turn_password"` TurnUserName string `yaml:"turn_username"`
UseTurn bool `yaml:"use_turn"` TurnPassword string `yaml:"turn_password"`
UseTurn bool `yaml:"use_turn"`
UsersLimit int `yaml:"user_limit"`
ClientsLimit int `yaml:"client_limit"`
NetworksLimit int `yaml:"network_limit"`
HostsLimit int `yaml:"host_limit"`
DeployedByOperator bool `yaml:"deployed_by_operator"`
} }
// ProxyMode - default proxy mode for server // ProxyMode - default proxy mode for server

View File

@@ -10,7 +10,7 @@
// //
// Schemes: https // Schemes: https
// BasePath: / // BasePath: /
// Version: 0.20.2 // Version: 0.20.3
// Host: netmaker.io // Host: netmaker.io
// //
// Consumes: // Consumes:

View File

@@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"reflect"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
@@ -26,11 +25,9 @@ func hostHandlers(r *mux.Router) {
r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(deleteHost))).Methods(http.MethodDelete) r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(deleteHost))).Methods(http.MethodDelete)
r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).Methods(http.MethodPost) r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).Methods(http.MethodPost)
r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(deleteHostFromNetwork))).Methods(http.MethodDelete) 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) r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
r.HandleFunc("/api/v1/host", authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet) r.HandleFunc("/api/v1/host", Authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet)
r.HandleFunc("/api/v1/host/{hostid}/signalpeer", authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost) r.HandleFunc("/api/v1/host/{hostid}/signalpeer", Authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost)
r.HandleFunc("/api/v1/auth-register/host", socketHandler) r.HandleFunc("/api/v1/auth-register/host", socketHandler)
} }
@@ -174,13 +171,6 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
} }
newHost := newHostData.ConvertAPIHostToNMHost(currHost) newHost := newHostData.ConvertAPIHostToNMHost(currHost)
// check if relay information is changed
updateRelay := false
if newHost.IsRelay && len(newHost.RelayedHosts) > 0 {
if len(newHost.RelayedHosts) != len(currHost.RelayedHosts) || !reflect.DeepEqual(newHost.RelayedHosts, currHost.RelayedHosts) {
updateRelay = true
}
}
logic.UpdateHost(newHost, currHost) // update the in memory struct values logic.UpdateHost(newHost, currHost) // update the in memory struct values
if err = logic.UpsertHost(newHost); err != nil { if err = logic.UpsertHost(newHost); err != nil {
@@ -188,9 +178,6 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return return
} }
if updateRelay {
logic.UpdateHostRelay(currHost.ID.String(), currHost.RelayedHosts, newHost.RelayedHosts)
}
// publish host update through MQ // publish host update through MQ
if err := mq.HostUpdate(&models.HostUpdate{ if err := mq.HostUpdate(&models.HostUpdate{
Action: models.UpdateHost, Action: models.UpdateHost,
@@ -244,33 +231,6 @@ func deleteHost(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return 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 { if err = logic.RemoveHost(currHost); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error()) logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))

View File

@@ -6,7 +6,6 @@ import (
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
) )
// limit consts // limit consts
@@ -23,20 +22,13 @@ func checkFreeTierLimits(limit_choice int, next http.Handler) http.HandlerFunc {
Code: http.StatusForbidden, Message: "free tier limits exceeded on networks", Code: http.StatusForbidden, Message: "free tier limits exceeded on networks",
} }
if logic.Free_Tier && servercfg.Is_EE { // check that free tier limits not exceeded if logic.Free_Tier { // check that free tier limits not exceeded
if limit_choice == networks_l { if limit_choice == networks_l {
currentNetworks, err := logic.GetNetworks() currentNetworks, err := logic.GetNetworks()
if (err != nil && !database.IsEmptyRecord(err)) || len(currentNetworks) >= logic.Networks_Limit { if (err != nil && !database.IsEmptyRecord(err)) || len(currentNetworks) >= logic.Networks_Limit {
logic.ReturnErrorResponse(w, r, errorResponse) logic.ReturnErrorResponse(w, r, errorResponse)
return return
} }
} else if limit_choice == node_l {
nodes, err := logic.GetAllNodes()
if (err != nil && !database.IsEmptyRecord(err)) || len(nodes) >= logic.Node_Limit {
errorResponse.Message = "free tier limits exceeded on nodes"
logic.ReturnErrorResponse(w, r, errorResponse)
return
}
} else if limit_choice == users_l { } else if limit_choice == users_l {
users, err := logic.GetUsers() users, err := logic.GetUsers()
if (err != nil && !database.IsEmptyRecord(err)) || len(users) >= logic.Users_Limit { if (err != nil && !database.IsEmptyRecord(err)) || len(users) >= logic.Users_Limit {

View File

@@ -23,18 +23,16 @@ var hostIDHeader = "host-id"
func nodeHandlers(r *mux.Router) { func nodeHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes", authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet) r.HandleFunc("/api/nodes", Authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet)
r.HandleFunc("/api/nodes/{network}", authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet) 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(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}", Authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPut)
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete) 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}/creategateway", Authorize(false, true, "user", http.HandlerFunc(createEgressGateway))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", authorize(false, true, "user", http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete) r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", Authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", authorize(false, true, "user", http.HandlerFunc(createEgressGateway))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods(http.MethodPost) r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete) r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost) r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost) r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost) r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost)
} }
@@ -154,7 +152,7 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
// even if it's technically ok // even if it's technically ok
// This is kind of a poor man's RBAC. There's probably a better/smarter way. // This is kind of a poor man's RBAC. There's probably a better/smarter way.
// TODO: Consider better RBAC implementations // TODO: Consider better RBAC implementations
func authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc { func Authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
var errorResponse = models.ErrorResponse{ var errorResponse = models.ErrorResponse{
Code: http.StatusForbidden, Message: logic.Forbidden_Msg, Code: http.StatusForbidden, Message: logic.Forbidden_Msg,
@@ -633,12 +631,12 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
} }
newNode := newData.ConvertToServerNode(&currentNode) newNode := newData.ConvertToServerNode(&currentNode)
relayupdate := false relayupdate := false
if currentNode.IsRelay && len(newNode.RelayAddrs) > 0 { if servercfg.Is_EE && newNode.IsRelay && len(newNode.RelayedNodes) > 0 {
if len(newNode.RelayAddrs) != len(currentNode.RelayAddrs) { if len(newNode.RelayedNodes) != len(currentNode.RelayedNodes) {
relayupdate = true relayupdate = true
} else { } else {
for i, addr := range newNode.RelayAddrs { for i, node := range newNode.RelayedNodes {
if addr != currentNode.RelayAddrs[i] { if node != currentNode.RelayedNodes[i] {
relayupdate = true relayupdate = true
} }
} }
@@ -651,10 +649,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return return
} }
relayedUpdate := false
if currentNode.IsRelayed && (currentNode.Address.String() != newNode.Address.String() || currentNode.Address6.String() != newNode.Address6.String()) {
relayedUpdate = true
}
ifaceDelta := logic.IfaceDelta(&currentNode, newNode) ifaceDelta := logic.IfaceDelta(&currentNode, newNode)
aclUpdate := currentNode.DefaultACL != newNode.DefaultACL aclUpdate := currentNode.DefaultACL != newNode.DefaultACL
if ifaceDelta && servercfg.Is_EE { if ifaceDelta && servercfg.Is_EE {
@@ -671,16 +665,13 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
return return
} }
if relayupdate { if relayupdate {
updatenodes := logic.UpdateRelay(currentNode.Network, currentNode.RelayAddrs, newNode.RelayAddrs) updatenodes := logic.UpdateRelayed(currentNode.ID.String(), currentNode.RelayedNodes, newNode.RelayedNodes)
if len(updatenodes) > 0 { if len(updatenodes) > 0 {
for _, relayedNode := range updatenodes { for _, relayedNode := range updatenodes {
runUpdates(&relayedNode, false) runUpdates(&relayedNode, false)
} }
} }
} }
if relayedUpdate {
updateRelay(&currentNode, newNode)
}
if servercfg.IsDNSMode() { if servercfg.IsDNSMode() {
logic.SetDNS() logic.SetDNS()
} }
@@ -690,8 +681,8 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode) json.NewEncoder(w).Encode(apiNode)
runUpdates(newNode, ifaceDelta) runUpdates(newNode, ifaceDelta)
go func(aclUpdate bool, newNode *models.Node) { go func(aclUpdate, relayupdate bool, newNode *models.Node) {
if aclUpdate { if aclUpdate || relayupdate {
if err := mq.PublishPeerUpdate(); err != nil { if err := mq.PublishPeerUpdate(); err != nil {
logger.Log(0, "error during node ACL update for node", newNode.ID.String()) logger.Log(0, "error during node ACL update for node", newNode.ID.String())
} }
@@ -699,7 +690,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
if err := mq.PublishReplaceDNS(&currentNode, newNode, host); err != nil { if err := mq.PublishReplaceDNS(&currentNode, newNode, host); err != nil {
logger.Log(1, "failed to publish dns update", err.Error()) logger.Log(1, "failed to publish dns update", err.Error())
} }
}(aclUpdate, newNode) }(aclUpdate, relayupdate, newNode)
} }
// swagger:route DELETE /api/nodes/{network}/{nodeid} nodes deleteNode // swagger:route DELETE /api/nodes/{network}/{nodeid} nodes deleteNode
@@ -743,6 +734,22 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
return return
} }
if node.IsRelayed {
// cleanup node from relayednodes on relay node
relayNode, err := logic.GetNodeByID(node.RelayedBy)
if err == nil {
relayedNodes := []string{}
for _, relayedNodeID := range relayNode.RelayedNodes {
if relayedNodeID == node.ID.String() {
continue
}
relayedNodes = append(relayedNodes, relayedNodeID)
}
relayNode.RelayedNodes = relayedNodes
logic.UpsertNode(&relayNode)
}
}
logic.ReturnSuccessResponse(w, r, nodeid+" deleted.") logic.ReturnSuccessResponse(w, r, nodeid+" deleted.")
logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"]) logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
if !fromNode { // notify node change if !fromNode { // notify node change
@@ -778,30 +785,6 @@ func runUpdates(node *models.Node, ifaceDelta bool) {
}() }()
} }
func updateRelay(oldnode, newnode *models.Node) {
relay := logic.FindRelay(oldnode)
newrelay := relay
//check if node's address has been updated and if so, update the relayAddrs of the relay node with the updated address of the relayed node
if oldnode.Address.String() != newnode.Address.String() {
for i, ip := range newrelay.RelayAddrs {
if ip == oldnode.Address.IP.String() {
newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], relay.RelayAddrs[i+1:]...)
newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address.IP.String())
}
}
}
//check if node's address(v6) has been updated and if so, update the relayAddrs of the relay node with the updated address(v6) of the relayed node
if oldnode.Address6.String() != newnode.Address6.String() {
for i, ip := range newrelay.RelayAddrs {
if ip == oldnode.Address.IP.String() {
newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], newrelay.RelayAddrs[i+1:]...)
newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address6.IP.String())
}
}
}
logic.UpdateNode(relay, newrelay)
}
func doesUserOwnNode(username, network, nodeID string) bool { func doesUserOwnNode(username, network, nodeID string) bool {
u, err := logic.GetUser(username) u, err := logic.GetUser(username)
if err != nil { if err != nil {

View File

@@ -1,199 +0,0 @@
package controller
import (
"encoding/json"
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
)
// swagger:route POST /api/nodes/{network}/{nodeid}/createrelay nodes createRelay
//
// Create a relay.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
func createRelay(w http.ResponseWriter, r *http.Request) {
var relay models.RelayRequest
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
err := json.NewDecoder(r.Body).Decode(&relay)
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
}
relay.NetID = params["network"]
relay.NodeID = params["nodeid"]
updatenodes, node, err := logic.CreateRelay(relay)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to create relay on node [%s] on network [%s]: %v", relay.NodeID, relay.NetID, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
logger.Log(1, r.Header.Get("user"), "created relay on node", relay.NodeID, "on network", relay.NetID)
for _, relayedNode := range updatenodes {
err = mq.NodeUpdate(&relayedNode)
if err != nil {
logger.Log(1, "error sending update to relayed node ", relayedNode.ID.String(), "on network", relay.NetID, ": ", err.Error())
}
}
apiNode := node.ConvertToAPINode()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
runUpdates(&node, true)
}
// swagger:route DELETE /api/nodes/{network}/{nodeid}/deleterelay nodes deleteRelay
//
// Remove a relay.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
func deleteRelay(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
nodeid := params["nodeid"]
netid := params["network"]
updatenodes, node, err := logic.DeleteRelay(netid, nodeid)
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
}
logger.Log(1, r.Header.Get("user"), "deleted relay server", nodeid, "on network", netid)
for _, relayedNode := range updatenodes {
err = mq.NodeUpdate(&relayedNode)
if err != nil {
logger.Log(1, "error sending update to relayed node ", relayedNode.ID.String(), "on network", netid, ": ", err.Error())
}
}
apiNode := node.ConvertToAPINode()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
runUpdates(&node, true)
}
// swagger:route POST /api/hosts/{hostid}/relay hosts createHostRelay
//
// Create a relay.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
func createHostRelay(w http.ResponseWriter, r *http.Request) {
var relay models.HostRelayRequest
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
err := json.NewDecoder(r.Body).Decode(&relay)
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
}
relay.HostID = params["hostid"]
relayHost, relayedHosts, err := logic.CreateHostRelay(relay)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to create relay on host [%s]: %v", relay.HostID, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
if err := mq.HostUpdate(&models.HostUpdate{
Action: models.UpdateHost,
Host: *relayHost,
}); err != nil {
logger.Log(0, "failed to send host update: ", relayHost.ID.String(), err.Error())
}
logger.Log(1, r.Header.Get("user"), "created relay on host", relay.HostID)
go func(relayHostID string) {
for _, relayedHost := range relayedHosts {
relayedHost.ProxyEnabled = true
logic.UpsertHost(&relayedHost)
if err := mq.HostUpdate(&models.HostUpdate{
Action: models.UpdateHost,
Host: relayedHost,
}); err != nil {
logger.Log(0, "failed to send host update: ", relayedHost.ID.String(), err.Error())
}
}
if err := mq.PublishPeerUpdate(); err != nil {
logger.Log(0, "fail to publish peer update: ", err.Error())
}
}(relay.HostID)
apiHostData := relayHost.ConvertNMHostToAPI()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiHostData)
}
// swagger:route DELETE /api/hosts/{hostid}/relay hosts deleteHostRelay
//
// Remove a relay.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
func deleteHostRelay(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
hostid := params["hostid"]
relayHost, relayed, err := logic.DeleteHostRelay(hostid)
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
}
logger.Log(1, r.Header.Get("user"), "deleted relay host", hostid)
go func() {
if err := mq.PublishPeerUpdate(); err != nil {
logger.Log(0, "fail to publish peer update: ", err.Error())
}
if err := mq.HostUpdate(&models.HostUpdate{
Action: models.UpdateHost,
Host: *relayHost,
}); err != nil {
logger.Log(0, "failed to send host update: ", relayHost.Name, err.Error())
}
for _, relayedHost := range relayed {
if err := mq.HostUpdate(&models.HostUpdate{
Action: models.UpdateHost,
Host: relayedHost,
}); err != nil {
logger.Log(0, "failed to send host update: ", relayedHost.Name, err.Error())
}
}
}()
apiHostData := relayHost.ConvertNMHostToAPI()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiHostData)
}

View File

@@ -20,8 +20,40 @@ func serverHandlers(r *mux.Router) {
resp.Write([]byte("Server is up and running!!")) resp.Write([]byte("Server is up and running!!"))
})) }))
r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))).Methods(http.MethodGet) r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))).Methods(http.MethodGet)
r.HandleFunc("/api/server/getserverinfo", authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods(http.MethodGet) r.HandleFunc("/api/server/getserverinfo", Authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods(http.MethodGet)
r.HandleFunc("/api/server/status", http.HandlerFunc(getStatus)).Methods(http.MethodGet) r.HandleFunc("/api/server/status", http.HandlerFunc(getStatus)).Methods(http.MethodGet)
r.HandleFunc("/api/server/usage", Authorize(true, false, "user", http.HandlerFunc(getUsage))).Methods(http.MethodGet)
}
func getUsage(w http.ResponseWriter, r *http.Request) {
type usage struct {
Hosts int `json:"hosts"`
Clients int `json:"clients"`
Networks int `json:"networks"`
Users int `json:"users"`
}
var serverUsage usage
hosts, err := logic.GetAllHosts()
if err == nil {
serverUsage.Hosts = len(hosts)
}
clients, err := logic.GetAllExtClients()
if err == nil {
serverUsage.Clients = len(clients)
}
users, err := logic.GetUsers()
if err == nil {
serverUsage.Users = len(users)
}
networks, err := logic.GetNetworks()
if err == nil {
serverUsage.Networks = len(networks)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(models.SuccessResponse{
Code: http.StatusOK,
Response: serverUsage,
})
} }
// swagger:route GET /api/server/status server getStatus // swagger:route GET /api/server/status server getStatus
@@ -41,6 +73,12 @@ func getStatus(w http.ResponseWriter, r *http.Request) {
type status struct { type status struct {
DB bool `json:"db_connected"` DB bool `json:"db_connected"`
Broker bool `json:"broker_connected"` Broker bool `json:"broker_connected"`
Usage struct {
Hosts int `json:"hosts"`
Clients int `json:"clients"`
Networks int `json:"networks"`
Users int `json:"users"`
} `json:"usage"`
} }
currentServerStatus := status{ currentServerStatus := status{

View File

@@ -19,6 +19,9 @@ var (
upgrader = websocket.Upgrader{} upgrader = websocket.Upgrader{}
) )
// verifyJWT makes logic.VerifyJWT fakeable/mockable in tests
var verifyJWT = logic.VerifyJWT
func userHandlers(r *mux.Router) { func userHandlers(r *mux.Router) {
r.HandleFunc("/api/users/adm/hasadmin", hasAdmin).Methods(http.MethodGet) r.HandleFunc("/api/users/adm/hasadmin", hasAdmin).Methods(http.MethodGet)
@@ -152,7 +155,7 @@ func getUser(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r) var params = mux.Vars(r)
usernameFetched := params["username"] usernameFetched := params["username"]
user, err := logic.GetUser(usernameFetched) user, err := logic.GetReturnUser(usernameFetched)
if err != nil { if err != nil {
logger.Log(0, usernameFetched, "failed to fetch user: ", err.Error()) logger.Log(0, usernameFetched, "failed to fetch user: ", err.Error())
@@ -230,7 +233,7 @@ func createAdmin(w http.ResponseWriter, r *http.Request) {
} }
logger.Log(1, admin.UserName, "was made a new admin") logger.Log(1, admin.UserName, "was made a new admin")
json.NewEncoder(w).Encode(admin) json.NewEncoder(w).Encode(logic.ToReturnUser(admin))
} }
// swagger:route POST /api/users/{username} user createUser // swagger:route POST /api/users/{username} user createUser
@@ -264,7 +267,7 @@ func createUser(w http.ResponseWriter, r *http.Request) {
return return
} }
logger.Log(1, user.UserName, "was created") logger.Log(1, user.UserName, "was created")
json.NewEncoder(w).Encode(user) json.NewEncoder(w).Encode(logic.ToReturnUser(user))
} }
// swagger:route PUT /api/users/networks/{username} user updateUserNetworks // swagger:route PUT /api/users/networks/{username} user updateUserNetworks
@@ -314,12 +317,13 @@ func updateUserNetworks(w http.ResponseWriter, r *http.Request) {
} }
logger.Log(1, username, "status was updated") logger.Log(1, username, "status was updated")
// re-read and return the new user struct // re-read and return the new user struct
if userChange, err = logic.GetUser(username); err != nil { returnUser, err := logic.GetReturnUser(username)
if err != nil {
logger.Log(0, username, "failed to fetch user: ", err.Error()) logger.Log(0, username, "failed to fetch user: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return return
} }
json.NewEncoder(w).Encode(userChange) json.NewEncoder(w).Encode(returnUser)
} }
// swagger:route PUT /api/users/{username} user updateUser // swagger:route PUT /api/users/{username} user updateUser
@@ -337,7 +341,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
// start here // start here
jwtUser, _, isadmin, err := logic.VerifyJWT(r.Header.Get("Authorization")) jwtUser, _, isadmin, err := verifyJWT(r.Header.Get("Authorization"))
if err != nil { if err != nil {
logger.Log(0, "verifyJWT error", err.Error()) logger.Log(0, "verifyJWT error", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
@@ -385,7 +389,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
return return
} }
logger.Log(1, username, "was updated") logger.Log(1, username, "was updated")
json.NewEncoder(w).Encode(user) json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
} }
// swagger:route PUT /api/users/{username}/adm user updateUserAdm // swagger:route PUT /api/users/{username}/adm user updateUserAdm
@@ -409,7 +413,7 @@ func updateUserAdm(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return return
} }
if auth.IsOauthUser(user) != nil { if auth.IsOauthUser(user) == nil {
err := fmt.Errorf("cannot update user info for oauth user %s", username) err := fmt.Errorf("cannot update user info for oauth user %s", username)
logger.Log(0, err.Error()) logger.Log(0, err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
@@ -436,7 +440,7 @@ func updateUserAdm(w http.ResponseWriter, r *http.Request) {
return return
} }
logger.Log(1, username, "was updated (admin)") logger.Log(1, username, "was updated (admin)")
json.NewEncoder(w).Encode(user) json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
} }
// swagger:route DELETE /api/users/{username} user deleteUser // swagger:route DELETE /api/users/{username} user deleteUser

View File

@@ -1,6 +1,12 @@
package controller package controller
import ( import (
"bytes"
"github.com/go-jose/go-jose/v3/json"
"github.com/gorilla/mux"
"io"
"net/http"
"net/http/httptest"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -19,6 +25,123 @@ func deleteAllUsers(t *testing.T) {
} }
} }
func TestGetUserNoHashedPassword(t *testing.T) {
// prepare existing user base
user := models.User{UserName: "freddie", Password: "password"}
haveOnlyOneUser(t, user)
// prepare request
rec, req := prepareUserRequest(t, models.User{}, user.UserName)
// test response
getUser(rec, req)
assertUserNameButNoPassword(t, rec.Body, user.UserName)
}
func TestCreateAdminNoHashedPassword(t *testing.T) {
// prepare existing user base
deleteAllUsers(t)
// prepare request
user := models.User{UserName: "jonathan", Password: "password"}
rec, req := prepareUserRequest(t, user, "")
// test response
createAdmin(rec, req)
assertUserNameButNoPassword(t, rec.Body, user.UserName)
}
func TestCreateUserNoHashedPassword(t *testing.T) {
// prepare existing user base
deleteAllUsers(t)
// prepare request
user := models.User{UserName: "jonathan", Password: "password"}
rec, req := prepareUserRequest(t, user, "")
// test response
createUser(rec, req)
assertUserNameButNoPassword(t, rec.Body, user.UserName)
}
func TestUpdateUserNetworksNoHashedPassword(t *testing.T) {
// prepare existing user base
user1 := models.User{UserName: "joestar", Password: "jonathan"}
haveOnlyOneUser(t, user1)
// prepare request
user2 := models.User{UserName: "joestar", Password: "joseph"}
rec, req := prepareUserRequest(t, user2, user1.UserName)
// test response
updateUserNetworks(rec, req)
assertUserNameButNoPassword(t, rec.Body, user1.UserName)
}
func TestUpdateUserNoHashedPassword(t *testing.T) {
// prepare existing user base
user1 := models.User{UserName: "dio", Password: "brando"}
haveOnlyOneUser(t, user1)
// prepare request
user2 := models.User{UserName: "giorno", Password: "giovanna"}
rec, req := prepareUserRequest(t, user2, user1.UserName)
// mock the jwt verification
oldVerify := verifyJWT
verifyJWT = func(bearerToken string) (username string, networks []string, isadmin bool, err error) {
return user1.UserName, user1.Networks, user1.IsAdmin, nil
}
defer func() { verifyJWT = oldVerify }()
// test response
updateUser(rec, req)
assertUserNameButNoPassword(t, rec.Body, user2.UserName)
}
func TestUpdateUserAdmNoHashedPassword(t *testing.T) {
// prepare existing user base
user1 := models.User{UserName: "dio", Password: "brando", IsAdmin: true}
haveOnlyOneUser(t, user1)
// prepare request
user2 := models.User{UserName: "giorno", Password: "giovanna"}
rec, req := prepareUserRequest(t, user2, user1.UserName)
// test response
updateUserAdm(rec, req)
assertUserNameButNoPassword(t, rec.Body, user2.UserName)
}
func prepareUserRequest(t *testing.T, userForBody models.User, userNameForParam string) (*httptest.ResponseRecorder, *http.Request) {
bits, err := json.Marshal(userForBody)
assert.Nil(t, err)
body := bytes.NewReader(bits)
rec := httptest.NewRecorder()
req := httptest.NewRequest("ANY", "https://example.com", body) // only the body matters here
req = mux.SetURLVars(req, map[string]string{"username": userNameForParam})
return rec, req
}
func haveOnlyOneUser(t *testing.T, user models.User) {
deleteAllUsers(t)
var err error
if user.IsAdmin {
err = logic.CreateAdmin(&user)
} else {
err = logic.CreateUser(&user)
}
assert.Nil(t, err)
}
func assertUserNameButNoPassword(t *testing.T, r io.Reader, userName string) {
var resp models.User
err := json.NewDecoder(r).Decode(&resp)
assert.Nil(t, err)
assert.Equal(t, userName, resp.UserName)
assert.Empty(t, resp.Password)
}
func TestHasAdmin(t *testing.T) { func TestHasAdmin(t *testing.T) {
// delete all current users // delete all current users
users, _ := logic.GetUsers() users, _ := logic.GetUsers()

107
ee/ee_controllers/relay.go Normal file
View File

@@ -0,0 +1,107 @@
package ee_controllers
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/gorilla/mux"
controller "github.com/gravitl/netmaker/controllers"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
)
// RelayHandlers - handle EE Relays
func RelayHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", controller.Authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", controller.Authorize(false, true, "user", http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete)
}
// swagger:route POST /api/nodes/{network}/{nodeid}/createrelay nodes createRelay
//
// Create a relay.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
func createRelay(w http.ResponseWriter, r *http.Request) {
var relayRequest models.RelayRequest
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
err := json.NewDecoder(r.Body).Decode(&relayRequest)
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
}
relayRequest.NetID = params["network"]
relayRequest.NodeID = params["nodeid"]
_, relayNode, err := logic.CreateRelay(relayRequest)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to create relay on node [%s] on network [%s]: %v", relayRequest.NodeID, relayRequest.NetID, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
go mq.PublishPeerUpdate()
logger.Log(1, r.Header.Get("user"), "created relay on node", relayRequest.NodeID, "on network", relayRequest.NetID)
apiNode := relayNode.ConvertToAPINode()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
}
// swagger:route DELETE /api/nodes/{network}/{nodeid}/deleterelay nodes deleteRelay
//
// Remove a relay.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
func deleteRelay(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
nodeid := params["nodeid"]
netid := params["network"]
updateNodes, node, err := logic.DeleteRelay(netid, nodeid)
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
}
logger.Log(1, r.Header.Get("user"), "deleted relay server", nodeid, "on network", netid)
go func() {
for _, relayedNode := range updateNodes {
err = mq.NodeUpdate(&relayedNode)
if err != nil {
logger.Log(1, "relayed node update ", relayedNode.ID.String(), "on network", relayedNode.Network, ": ", err.Error())
}
h, err := logic.GetHost(relayedNode.HostID.String())
if err == nil {
if h.OS == models.OS_Types.IoT {
node.IsRelay = true // for iot update to recognise that it has to delete relay peer
if err = mq.PublishSingleHostPeerUpdate(context.Background(), h, &node, nil); err != nil {
logger.Log(1, "failed to publish peer update to host", h.ID.String(), ": ", err.Error())
}
}
}
}
mq.PublishPeerUpdate()
}()
logger.Log(1, r.Header.Get("user"), "deleted relay on node", node.ID.String(), "on network", node.Network)
apiNode := node.ConvertToAPINode()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
}

View File

@@ -16,23 +16,20 @@ import (
// InitEE - Initialize EE Logic // InitEE - Initialize EE Logic
func InitEE() { func InitEE() {
setIsEnterprise() setIsEnterprise()
servercfg.Is_EE = true
models.SetLogo(retrieveEELogo()) models.SetLogo(retrieveEELogo())
controller.HttpHandlers = append( controller.HttpHandlers = append(
controller.HttpHandlers, controller.HttpHandlers,
ee_controllers.MetricHandlers, ee_controllers.MetricHandlers,
ee_controllers.NetworkUsersHandlers, ee_controllers.NetworkUsersHandlers,
ee_controllers.UserGroupsHandlers, ee_controllers.UserGroupsHandlers,
ee_controllers.RelayHandlers,
) )
logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() { logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
// == License Handling == // == License Handling ==
ValidateLicense() ValidateLicense()
if Limits.FreeTier { logger.Log(0, "proceeding with Paid Tier license")
logger.Log(0, "proceeding with Free Tier license") logic.SetFreeTierForTelemetry(false)
logic.SetFreeTierForTelemetry(true)
} else {
logger.Log(0, "proceeding with Paid Tier license")
logic.SetFreeTierForTelemetry(false)
}
// == End License Handling == // == End License Handling ==
AddLicenseHooks() AddLicenseHooks()
resetFailover() resetFailover()
@@ -45,17 +42,6 @@ func InitEE() {
logic.AllowClientNodeAccess = eelogic.RemoveDeniedNodeFromClient logic.AllowClientNodeAccess = eelogic.RemoveDeniedNodeFromClient
} }
func setControllerLimits() {
logic.Node_Limit = Limits.Nodes
logic.Users_Limit = Limits.Users
logic.Clients_Limit = Limits.Clients
logic.Free_Tier = Limits.FreeTier
servercfg.Is_EE = true
if logic.Free_Tier {
logic.Networks_Limit = 3
}
}
func resetFailover() { func resetFailover() {
nets, err := logic.GetNetworks() nets, err := logic.GetNetworks()
if err == nil { if err == nil {

View File

@@ -9,12 +9,13 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"math"
"net/http" "net/http"
"time"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/ncutils" "github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
"golang.org/x/crypto/nacl/box" "golang.org/x/crypto/nacl/box"
@@ -31,8 +32,14 @@ type apiServerConf struct {
// AddLicenseHooks - adds the validation and cache clear hooks // AddLicenseHooks - adds the validation and cache clear hooks
func AddLicenseHooks() { func AddLicenseHooks() {
logic.AddHook(ValidateLicense) logic.HookManagerCh <- models.HookDetails{
logic.AddHook(ClearLicenseCache) Hook: ValidateLicense,
Interval: time.Hour,
}
logic.HookManagerCh <- models.HookDetails{
Hook: ClearLicenseCache,
Interval: time.Hour,
}
} }
// ValidateLicense - the initial license check for netmaker server // ValidateLicense - the initial license check for netmaker server
@@ -58,8 +65,8 @@ func ValidateLicense() error {
} }
licenseSecret := LicenseSecret{ licenseSecret := LicenseSecret{
UserID: netmakerAccountID, AssociatedID: netmakerAccountID,
Limits: getCurrentServerLimit(), Limits: getCurrentServerLimit(),
} }
secretData, err := json.Marshal(&licenseSecret) secretData, err := json.Marshal(&licenseSecret)
@@ -92,17 +99,6 @@ func ValidateLicense() error {
logger.FatalLog0(errValidation.Error()) logger.FatalLog0(errValidation.Error())
} }
Limits.Networks = math.MaxInt
Limits.FreeTier = license.FreeTier == "yes"
Limits.Clients = license.LimitClients
Limits.Nodes = license.LimitNodes
Limits.Servers = license.LimitServers
Limits.Users = license.LimitUsers
if Limits.FreeTier {
Limits.Networks = 3
}
setControllerLimits()
logger.Log(0, "License validation succeeded!") logger.Log(0, "License validation succeeded!")
return nil return nil
} }
@@ -167,6 +163,7 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, erro
} }
msg := ValidateLicenseRequest{ msg := ValidateLicenseRequest{
LicenseKey: servercfg.GetLicenseKey(),
NmServerPubKey: base64encode(publicKeyBytes), NmServerPubKey: base64encode(publicKeyBytes),
EncryptedPart: base64encode(encryptedData), EncryptedPart: base64encode(encryptedData),
} }
@@ -180,9 +177,6 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
reqParams := req.URL.Query()
reqParams.Add("licensevalue", servercfg.GetLicenseKey())
req.URL.RawQuery = reqParams.Encode()
req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json") req.Header.Add("Accept", "application/json")
client := &http.Client{} client := &http.Client{}

View File

@@ -3,7 +3,7 @@ package ee
import "fmt" import "fmt"
const ( const (
api_endpoint = "https://api.controller.netmaker.io/api/v1/license/validate" api_endpoint = "https://api.accounts.netmaker.io/api/v1/license/validate"
license_cache_key = "license_response_cache" license_cache_key = "license_response_cache"
license_validation_err_msg = "invalid license" license_validation_err_msg = "invalid license"
server_id_key = "nm-server-id" server_id_key = "nm-server-id"
@@ -11,38 +11,17 @@ const (
var errValidation = fmt.Errorf(license_validation_err_msg) var errValidation = fmt.Errorf(license_validation_err_msg)
// Limits - limits to be referenced throughout server
var Limits = GlobalLimits{
Servers: 0,
Users: 0,
Nodes: 0,
Clients: 0,
Networks: 0,
FreeTier: false,
}
// GlobalLimits - struct for holding global limits on this netmaker server in memory
type GlobalLimits struct {
Servers int
Users int
Nodes int
Clients int
FreeTier bool
Networks int
}
// LicenseKey - the license key struct representation with associated data // LicenseKey - the license key struct representation with associated data
type LicenseKey struct { type LicenseKey struct {
LicenseValue string `json:"license_value"` // actual (public) key and the unique value for the key LicenseValue string `json:"license_value"` // actual (public) key and the unique value for the key
Expiration int64 `json:"expiration"` Expiration int64 `json:"expiration"`
LimitServers int `json:"limit_servers"` LimitServers int `json:"limit_servers"`
LimitUsers int `json:"limit_users"` LimitUsers int `json:"limit_users"`
LimitNodes int `json:"limit_nodes"` LimitHosts int `json:"limit_hosts"`
LimitClients int `json:"limit_clients"` LimitNetworks int `json:"limit_networks"`
Metadata string `json:"metadata"` LimitClients int `json:"limit_clients"`
SubscriptionID string `json:"subscription_id"` // for a paid subscription (non-free-tier license) Metadata string `json:"metadata"`
FreeTier string `json:"free_tier"` // yes if free tier IsActive bool `json:"is_active"` // yes if active
IsActive string `json:"is_active"` // yes if active
} }
// ValidatedLicense - the validated license struct // ValidatedLicense - the validated license struct
@@ -53,28 +32,31 @@ type ValidatedLicense struct {
// LicenseSecret - the encrypted struct for sending user-id // LicenseSecret - the encrypted struct for sending user-id
type LicenseSecret struct { type LicenseSecret struct {
UserID string `json:"user_id" binding:"required"` // UUID for user foreign key to User table AssociatedID string `json:"associated_id" binding:"required"` // UUID for user foreign key to User table
Limits LicenseLimits `json:"limits" binding:"required"` Limits LicenseLimits `json:"limits" binding:"required"`
} }
// LicenseLimits - struct license limits // LicenseLimits - struct license limits
type LicenseLimits struct { type LicenseLimits struct {
Servers int `json:"servers" binding:"required"` Servers int `json:"servers"`
Users int `json:"users" binding:"required"` Users int `json:"users"`
Nodes int `json:"nodes" binding:"required"` Hosts int `json:"hosts"`
Clients int `json:"clients" binding:"required"` Clients int `json:"clients"`
Networks int `json:"networks"`
} }
// LicenseLimits.SetDefaults - sets the default values for limits // LicenseLimits.SetDefaults - sets the default values for limits
func (l *LicenseLimits) SetDefaults() { func (l *LicenseLimits) SetDefaults() {
l.Clients = 0 l.Clients = 0
l.Servers = 1 l.Servers = 1
l.Nodes = 0 l.Hosts = 0
l.Users = 1 l.Users = 1
l.Networks = 0
} }
// ValidateLicenseRequest - used for request to validate license endpoint // ValidateLicenseRequest - used for request to validate license endpoint
type ValidateLicenseRequest struct { type ValidateLicenseRequest struct {
LicenseKey string `json:"license_key" binding:"required"`
NmServerPubKey string `json:"nm_server_pub_key" binding:"required"` // Netmaker server public key used to send data back to Netmaker for the Netmaker server to decrypt (eg output from validating license) NmServerPubKey string `json:"nm_server_pub_key" binding:"required"` // Netmaker server public key used to send data back to Netmaker for the Netmaker server to decrypt (eg output from validating license)
EncryptedPart string `json:"secret" binding:"required"` EncryptedPart string `json:"secret" binding:"required"`
} }

View File

@@ -30,12 +30,11 @@ func base64decode(input string) []byte {
return bytes return bytes
} }
func getCurrentServerLimit() (limits LicenseLimits) { func getCurrentServerLimit() (limits LicenseLimits) {
limits.SetDefaults() limits.SetDefaults()
nodes, err := logic.GetAllNodes() hosts, err := logic.GetAllHosts()
if err == nil { if err == nil {
limits.Nodes = len(nodes) limits.Hosts = len(hosts)
} }
clients, err := logic.GetAllExtClients() clients, err := logic.GetAllExtClients()
if err == nil { if err == nil {
@@ -45,5 +44,9 @@ func getCurrentServerLimit() (limits LicenseLimits) {
if err == nil { if err == nil {
limits.Users = len(users) limits.Users = len(users)
} }
networks, err := logic.GetNetworks()
if err == nil {
limits.Networks = len(networks)
}
return return
} }

10
go.mod
View File

@@ -15,11 +15,11 @@ require (
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/txn2/txeh v1.4.0 github.com/txn2/txeh v1.4.0
golang.org/x/crypto v0.9.0 golang.org/x/crypto v0.10.0
golang.org/x/net v0.10.0 // indirect golang.org/x/net v0.11.0 // indirect
golang.org/x/oauth2 v0.8.0 golang.org/x/oauth2 v0.9.0
golang.org/x/sys v0.8.0 // indirect golang.org/x/sys v0.9.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.10.0 // indirect
golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c // indirect golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.28.1 // indirect

20
go.sum
View File

@@ -121,8 +121,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -133,10 +133,10 @@ golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -152,8 +152,8 @@ golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -161,8 +161,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -16,7 +16,7 @@ spec:
hostNetwork: true hostNetwork: true
containers: containers:
- name: netclient - name: netclient
image: gravitl/netclient:v0.20.2 image: gravitl/netclient:v0.20.3
env: env:
- name: TOKEN - name: TOKEN
value: "TOKEN_VALUE" value: "TOKEN_VALUE"

View File

@@ -28,7 +28,7 @@ spec:
# - "<node label value>" # - "<node label value>"
containers: containers:
- name: netclient - name: netclient
image: gravitl/netclient:v0.20.2 image: gravitl/netclient:v0.20.3
env: env:
- name: TOKEN - name: TOKEN
value: "TOKEN_VALUE" value: "TOKEN_VALUE"

View File

@@ -15,7 +15,7 @@ spec:
spec: spec:
containers: containers:
- name: netmaker-ui - name: netmaker-ui
image: gravitl/netmaker-ui:v0.20.2 image: gravitl/netmaker-ui:v0.20.3
ports: ports:
- containerPort: 443 - containerPort: 443
env: env:

View File

@@ -42,20 +42,6 @@ func HasAdmin() (bool, error) {
return false, err return false, err
} }
// GetReturnUser - gets a user
func GetReturnUser(username string) (models.ReturnUser, error) {
var user models.ReturnUser
record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
if err != nil {
return user, err
}
if err = json.Unmarshal([]byte(record), &user); err != nil {
return models.ReturnUser{}, err
}
return user, err
}
// GetUsers - gets users // GetUsers - gets users
func GetUsers() ([]models.ReturnUser, error) { func GetUsers() ([]models.ReturnUser, error) {

View File

@@ -102,6 +102,9 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq
if err != nil { if err != nil {
return models.Node{}, err return models.Node{}, err
} }
if node.IsRelayed {
return models.Node{}, errors.New("ingress cannot be created on a relayed node")
}
host, err := GetHost(node.HostID.String()) host, err := GetHost(node.HostID.String())
if err != nil { if err != nil {
return models.Node{}, err return models.Node{}, err

View File

@@ -93,7 +93,14 @@ func GetHost(hostid string) (*models.Host, error) {
// CreateHost - creates a host if not exist // CreateHost - creates a host if not exist
func CreateHost(h *models.Host) error { func CreateHost(h *models.Host) error {
_, err := GetHost(h.ID.String()) hosts, err := GetAllHosts()
if err != nil && !database.IsEmptyRecord(err) {
return err
}
if len(hosts) >= Hosts_Limit {
return errors.New("free tier limits exceeded on hosts")
}
_, err = GetHost(h.ID.String())
if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) { if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) {
return ErrHostExists return ErrHostExists
} }

View File

@@ -75,6 +75,16 @@ func UpdateNodeCheckin(node *models.Node) error {
return database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME) return database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
} }
// UpsertNode - updates node in the DB
func UpsertNode(newNode *models.Node) error {
newNode.SetLastModified()
data, err := json.Marshal(newNode)
if err != nil {
return err
}
return database.Insert(newNode.ID.String(), string(data), database.NODES_TABLE_NAME)
}
// UpdateNode - takes a node and updates another node with it's values // UpdateNode - takes a node and updates another node with it's values
func UpdateNode(currentNode *models.Node, newNode *models.Node) error { func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
if newNode.Address.IP.String() != currentNode.Address.IP.String() { if newNode.Address.IP.String() != currentNode.Address.IP.String() {
@@ -85,7 +95,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
} }
} }
nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL
newNode.Fill(currentNode) newNode.Fill(currentNode, servercfg.Is_EE)
// check for un-settable server values // check for un-settable server values
if err := ValidateNode(newNode, true); err != nil { if err := ValidateNode(newNode, true); err != nil {
@@ -338,34 +348,6 @@ func GetDeletedNodeByMacAddress(network string, macaddress string) (models.Node,
return node, nil return node, nil
} }
// GetNodeRelay - gets the relay node of a given network
func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
var relay models.Node
if err != nil {
if database.IsEmptyRecord(err) {
return relay, nil
}
logger.Log(2, err.Error())
return relay, err
}
for _, value := range collection {
err := json.Unmarshal([]byte(value), &relay)
if err != nil {
logger.Log(2, err.Error())
continue
}
if relay.IsRelay {
for _, addr := range relay.RelayAddrs {
if addr == relayedNodeAddr {
return relay, nil
}
}
}
}
return relay, errors.New(RELAY_NODE_ERR + " " + relayedNodeAddr)
}
func GetNodeByID(uuid string) (models.Node, error) { func GetNodeByID(uuid string) (models.Node, error) {
var record, err = database.FetchRecord(database.NODES_TABLE_NAME, uuid) var record, err = database.FetchRecord(database.NODES_TABLE_NAME, uuid)
if err != nil { if err != nil {
@@ -399,24 +381,12 @@ func GetDeletedNodeByID(uuid string) (models.Node, error) {
// FindRelay - returns the node that is the relay for a relayed node // FindRelay - returns the node that is the relay for a relayed node
func FindRelay(node *models.Node) *models.Node { func FindRelay(node *models.Node) *models.Node {
if !node.IsRelayed { relay, err := GetNodeByID(node.RelayedBy)
return nil
}
peers, err := GetNetworkNodes(node.Network)
if err != nil { if err != nil {
logger.Log(0, "FindRelay: "+err.Error())
return nil return nil
} }
for _, peer := range peers { return &relay
if !peer.IsRelay {
continue
}
for _, ip := range peer.RelayAddrs {
if ip == node.Address.IP.String() || ip == node.Address6.IP.String() {
return &peer
}
}
}
return nil
} }
// GetNetworkIngresses - gets the gateways of a network // GetNetworkIngresses - gets the gateways of a network

View File

@@ -13,6 +13,7 @@ import (
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"golang.org/x/exp/slog"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
) )
@@ -29,41 +30,6 @@ func GetProxyUpdateForHost(ctx context.Context, host *models.Host) (models.Proxy
Action: models.ProxyUpdate, Action: models.ProxyUpdate,
} }
peerConfMap := make(map[string]models.PeerConf) peerConfMap := make(map[string]models.PeerConf)
if host.IsRelayed {
relayHost, err := GetHost(host.RelayedBy)
if err == nil {
relayEndpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayHost.EndpointIP, GetPeerListenPort(relayHost)))
if err != nil {
logger.Log(1, "failed to resolve relay node endpoint: ", err.Error())
}
proxyPayload.IsRelayed = true
proxyPayload.RelayedTo = relayEndpoint
} else {
logger.Log(0, "couldn't find relay host for: ", host.ID.String())
}
}
if host.IsRelay {
relayedHosts := GetRelayedHosts(host)
relayPeersMap := make(map[string]models.RelayedConf)
for _, relayedHost := range relayedHosts {
relayedHost := relayedHost
payload, err := GetPeerUpdateForHost(ctx, "", &relayedHost, nil, nil)
if err == nil {
relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedHost.EndpointIP, GetPeerListenPort(&relayedHost)))
if udpErr == nil {
relayPeersMap[relayedHost.PublicKey.String()] = models.RelayedConf{
RelayedPeerEndpoint: relayedEndpoint,
RelayedPeerPubKey: relayedHost.PublicKey.String(),
Peers: payload.Peers,
}
}
}
}
proxyPayload.IsRelay = true
proxyPayload.RelayedPeerConf = relayPeersMap
}
var ingressStatus bool var ingressStatus bool
for _, nodeID := range host.Nodes { for _, nodeID := range host.Nodes {
@@ -101,18 +67,6 @@ func GetProxyUpdateForHost(ctx context.Context, host *models.Host) (models.Proxy
} }
} }
if peerHost.IsRelayed && peerHost.RelayedBy != host.ID.String() {
relayHost, err := GetHost(peerHost.RelayedBy)
if err == nil {
relayTo, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayHost.EndpointIP, GetPeerListenPort(relayHost)))
if err == nil {
currPeerConf.IsRelayed = true
currPeerConf.RelayedTo = relayTo
}
}
}
peerConfMap[peerHost.PublicKey.String()] = currPeerConf peerConfMap[peerHost.PublicKey.String()] = currPeerConf
} }
if node.IsIngressGateway { if node.IsIngressGateway {
@@ -167,7 +121,9 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
HostNetworkInfo: models.HostInfoMap{}, HostNetworkInfo: models.HostInfoMap{},
} }
logger.Log(1, "peer update for host", host.ID.String()) // endpoint detection always comes from the server
hostPeerUpdate.EndpointDetection = servercfg.EndpointDetectionEnabled()
slog.Debug("peer update for host", "hostId", host.ID.String())
peerIndexMap := make(map[string]int) peerIndexMap := make(map[string]int)
for _, nodeID := range host.Nodes { for _, nodeID := range host.Nodes {
nodeID := nodeID nodeID := nodeID
@@ -178,6 +134,61 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE { if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE {
continue continue
} }
if host.OS == models.OS_Types.IoT {
hostPeerUpdate.NodeAddrs = append(hostPeerUpdate.NodeAddrs, node.PrimaryAddressIPNet())
if node.IsRelayed {
relayNode, err := GetNodeByID(node.RelayedBy)
if err != nil {
continue
}
relayHost, err := GetHost(relayNode.HostID.String())
if err != nil {
continue
}
relayPeer := wgtypes.PeerConfig{
PublicKey: relayHost.PublicKey,
PersistentKeepaliveInterval: &relayNode.PersistentKeepalive,
ReplaceAllowedIPs: true,
AllowedIPs: GetAllowedIPs(&node, &relayNode, nil),
}
uselocal := false
if host.EndpointIP.String() == relayHost.EndpointIP.String() {
// peer is on same network
// set to localaddress
uselocal = true
if node.LocalAddress.IP == nil {
// use public endpint
uselocal = false
}
if node.LocalAddress.String() == relayNode.LocalAddress.String() {
uselocal = false
}
}
relayPeer.Endpoint = &net.UDPAddr{
IP: relayHost.EndpointIP,
Port: getPeerWgListenPort(relayHost),
}
if uselocal {
relayPeer.Endpoint.IP = relayNode.LocalAddress.IP
relayPeer.Endpoint.Port = relayHost.ListenPort
}
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, relayPeer)
} else if deletedNode != nil && deletedNode.IsRelay {
relayHost, err := GetHost(deletedNode.HostID.String())
if err != nil {
continue
}
relayPeer := wgtypes.PeerConfig{
PublicKey: relayHost.PublicKey,
Remove: true,
}
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, relayPeer)
}
continue
}
currentPeers := GetNetworkNodesMemory(allNodes, node.Network) currentPeers := GetNetworkNodesMemory(allNodes, node.Network)
var nodePeerMap map[string]models.PeerRouteInfo var nodePeerMap map[string]models.PeerRouteInfo
if node.IsIngressGateway || node.IsEgressGateway { if node.IsIngressGateway || node.IsEgressGateway {
@@ -195,58 +206,17 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
//skip yourself //skip yourself
continue continue
} }
var peerConfig wgtypes.PeerConfig
peerHost, err := GetHost(peer.HostID.String()) peerHost, err := GetHost(peer.HostID.String())
if err != nil { if err != nil {
logger.Log(1, "no peer host", peer.HostID.String(), err.Error()) logger.Log(1, "no peer host", peer.HostID.String(), err.Error())
return models.HostPeerUpdate{}, err return models.HostPeerUpdate{}, err
} }
peerConfig := wgtypes.PeerConfig{
peerConfig.PublicKey = peerHost.PublicKey PublicKey: peerHost.PublicKey,
peerConfig.PersistentKeepaliveInterval = &peer.PersistentKeepalive PersistentKeepaliveInterval: &peer.PersistentKeepalive,
peerConfig.ReplaceAllowedIPs = true ReplaceAllowedIPs: true,
uselocal := false
if host.EndpointIP.String() == peerHost.EndpointIP.String() {
// peer is on same network
// set to localaddress
uselocal = true
if node.LocalAddress.IP == nil {
// use public endpint
uselocal = false
}
if node.LocalAddress.String() == peer.LocalAddress.String() {
uselocal = false
}
} }
peerConfig.Endpoint = &net.UDPAddr{
IP: peerHost.EndpointIP,
Port: getPeerWgListenPort(peerHost),
}
if uselocal {
peerConfig.Endpoint.IP = peer.LocalAddress.IP
peerConfig.Endpoint.Port = peerHost.ListenPort
}
allowedips := GetAllowedIPs(&node, &peer, nil)
if peer.IsIngressGateway {
for _, entry := range peer.IngressGatewayRange {
_, cidr, err := net.ParseCIDR(string(entry))
if err == nil {
allowedips = append(allowedips, *cidr)
}
}
}
if peer.IsEgressGateway {
allowedips = append(allowedips, getEgressIPs(&node, &peer)...)
}
if peer.Action != models.NODE_DELETE &&
!peer.PendingDelete &&
peer.Connected &&
nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(peer.ID.String())) &&
(deletedNode == nil || (deletedNode != nil && peer.ID.String() != deletedNode.ID.String())) {
peerConfig.AllowedIPs = allowedips // only append allowed IPs if valid connection
}
if node.IsIngressGateway || node.IsEgressGateway { if node.IsIngressGateway || node.IsEgressGateway {
if peer.IsIngressGateway { if peer.IsIngressGateway {
_, extPeerIDAndAddrs, err := getExtPeers(&peer) _, extPeerIDAndAddrs, err := getExtPeers(&peer)
@@ -279,6 +249,47 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
ID: peer.ID.String(), ID: peer.ID.String(),
} }
} }
if (node.IsRelayed && node.RelayedBy != peer.ID.String()) || (peer.IsRelayed && peer.RelayedBy != node.ID.String()) {
// if node is relayed and peer is not the relay, set remove to true
if _, ok := hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()]; ok {
continue
}
peerConfig.Remove = true
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig)
peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1
continue
}
uselocal := false
if host.EndpointIP.String() == peerHost.EndpointIP.String() {
// peer is on same network
// set to localaddress
uselocal = true
if node.LocalAddress.IP == nil {
// use public endpint
uselocal = false
}
if node.LocalAddress.String() == peer.LocalAddress.String() {
uselocal = false
}
}
peerConfig.Endpoint = &net.UDPAddr{
IP: peerHost.EndpointIP,
Port: getPeerWgListenPort(peerHost),
}
if uselocal {
peerConfig.Endpoint.IP = peer.LocalAddress.IP
peerConfig.Endpoint.Port = peerHost.ListenPort
}
allowedips := GetAllowedIPs(&node, &peer, nil)
if peer.Action != models.NODE_DELETE &&
!peer.PendingDelete &&
peer.Connected &&
nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(peer.ID.String())) &&
(deletedNode == nil || (deletedNode != nil && peer.ID.String() != deletedNode.ID.String())) {
peerConfig.AllowedIPs = allowedips // only append allowed IPs if valid connection
}
peerProxyPort := GetProxyListenPort(peerHost) peerProxyPort := GetProxyListenPort(peerHost)
var nodePeer wgtypes.PeerConfig var nodePeer wgtypes.PeerConfig
@@ -300,8 +311,9 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host
nodePeer = peerConfig nodePeer = peerConfig
} else { } else {
peerAllowedIPs := hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs peerAllowedIPs := hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs
peerAllowedIPs = append(peerAllowedIPs, allowedips...) peerAllowedIPs = append(peerAllowedIPs, peerConfig.AllowedIPs...)
hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs = peerAllowedIPs hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs = peerAllowedIPs
hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].Remove = false
hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()][peer.ID.String()] = models.IDandAddr{ hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()][peer.ID.String()] = models.IDandAddr{
ID: peer.ID.String(), ID: peer.ID.String(),
Address: peer.PrimaryAddress(), Address: peer.PrimaryAddress(),
@@ -624,14 +636,15 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
} }
} }
} }
if node.IsRelayed && node.RelayedBy == peer.ID.String() {
allowedips = append(allowedips, getAllowedIpsForRelayed(node, peer)...)
}
return allowedips return allowedips
} }
func getEgressIPs(node, peer *models.Node) []net.IPNet { func getEgressIPs(peer *models.Node) []net.IPNet {
host, err := GetHost(node.HostID.String())
if err != nil {
logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error())
}
peerHost, err := GetHost(peer.HostID.String()) peerHost, err := GetHost(peer.HostID.String())
if err != nil { if err != nil {
logger.Log(0, "error retrieving host for peer", peer.ID.String(), err.Error()) logger.Log(0, "error retrieving host for peer", peer.ID.String(), err.Error())
@@ -651,12 +664,12 @@ func getEgressIPs(node, peer *models.Node) []net.IPNet {
} }
// getting the public ip of node // getting the public ip of node
if ipnet.Contains(peerHost.EndpointIP) && !internetGateway { // ensuring egress gateway range does not contain endpoint of node if ipnet.Contains(peerHost.EndpointIP) && !internetGateway { // ensuring egress gateway range does not contain endpoint of node
logger.Log(2, "egress IP range of ", iprange, " overlaps with ", host.EndpointIP.String(), ", omitting") logger.Log(2, "egress IP range of ", iprange, " overlaps with ", peerHost.EndpointIP.String(), ", omitting")
continue // skip adding egress range if overlaps with node's ip continue // skip adding egress range if overlaps with node's ip
} }
// TODO: Could put in a lot of great logic to avoid conflicts / bad routes // TODO: Could put in a lot of great logic to avoid conflicts / bad routes
if ipnet.Contains(node.LocalAddress.IP) && !internetGateway { // ensuring egress gateway range does not contain public ip of node if ipnet.Contains(peer.LocalAddress.IP) && !internetGateway { // ensuring egress gateway range does not contain public ip of node
logger.Log(2, "egress IP range of ", iprange, " overlaps with ", node.LocalAddress.String(), ", omitting") logger.Log(2, "egress IP range of ", iprange, " overlaps with ", peer.LocalAddress.String(), ", omitting")
continue // skip adding egress range if overlaps with node's local ip continue // skip adding egress range if overlaps with node's local ip
} }
if err != nil { if err != nil {
@@ -687,12 +700,50 @@ func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
// handle egress gateway peers // handle egress gateway peers
if peer.IsEgressGateway { if peer.IsEgressGateway {
//hasGateway = true //hasGateway = true
egressIPs := getEgressIPs(node, peer) egressIPs := getEgressIPs(peer)
allowedips = append(allowedips, egressIPs...) allowedips = append(allowedips, egressIPs...)
} }
if peer.IsRelay {
for _, relayedNodeID := range peer.RelayedNodes {
if node.ID.String() == relayedNodeID {
continue
}
relayedNode, err := GetNodeByID(relayedNodeID)
if err != nil {
continue
}
allowed := getRelayedAddresses(relayedNodeID)
if relayedNode.IsEgressGateway {
allowed = append(allowed, getEgressIPs(&relayedNode)...)
}
allowedips = append(allowedips, allowed...)
}
}
return allowedips return allowedips
} }
// getAllowedIpsForRelayed - returns the peerConfig for a node relayed by relay
func getAllowedIpsForRelayed(relayed, relay *models.Node) (allowedIPs []net.IPNet) {
if relayed.RelayedBy != relay.ID.String() {
logger.Log(0, "RelayedByRelay called with invalid parameters")
return
}
peers, err := GetNetworkNodes(relay.Network)
if err != nil {
logger.Log(0, "error getting network clients", err.Error())
return
}
for _, peer := range peers {
if peer.ID == relayed.ID || peer.ID == relay.ID {
continue
}
if nodeacls.AreNodesAllowed(nodeacls.NetworkID(relayed.Network), nodeacls.NodeID(relayed.ID.String()), nodeacls.NodeID(peer.ID.String())) {
allowedIPs = append(allowedIPs, GetAllowedIPs(relayed, &peer, nil)...)
}
}
return
}
func getCIDRMaskFromAddr(addr string) net.IPMask { func getCIDRMaskFromAddr(addr string) net.IPMask {
cidr := net.CIDRMask(32, 32) cidr := net.CIDRMask(32, 32)
ipAddr, err := netip.ParseAddr(addr) ipAddr, err := netip.ParseAddr(addr)

View File

@@ -4,7 +4,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"time" "net"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
@@ -31,8 +31,7 @@ func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error)
return returnnodes, models.Node{}, err return returnnodes, models.Node{}, err
} }
node.IsRelay = true node.IsRelay = true
node.RelayAddrs = relay.RelayAddrs node.RelayedNodes = relay.RelayedNodes
node.SetLastModified() node.SetLastModified()
nodeData, err := json.Marshal(&node) nodeData, err := json.Marshal(&node)
if err != nil { if err != nil {
@@ -41,144 +40,98 @@ func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error)
if err = database.Insert(node.ID.String(), string(nodeData), database.NODES_TABLE_NAME); err != nil { if err = database.Insert(node.ID.String(), string(nodeData), database.NODES_TABLE_NAME); err != nil {
return returnnodes, models.Node{}, err return returnnodes, models.Node{}, err
} }
returnnodes, err = SetRelayedNodes(true, node.Network, node.RelayAddrs) returnnodes = SetRelayedNodes(true, relay.NodeID, relay.RelayedNodes)
if err != nil { for _, relayedNode := range returnnodes {
return returnnodes, node, err data, err := json.Marshal(&relayedNode)
if err != nil {
logger.Log(0, "marshalling relayed node", err.Error())
continue
}
if err := database.Insert(relayedNode.ID.String(), string(data), database.NODES_TABLE_NAME); err != nil {
logger.Log(0, "inserting relayed node", err.Error())
continue
}
} }
return returnnodes, node, nil return returnnodes, node, nil
} }
// CreateHostRelay - creates a host relay // SetRelayedNodes- sets and saves node as relayed
func CreateHostRelay(relay models.HostRelayRequest) (relayHost *models.Host, relayedHosts []models.Host, err error) { func SetRelayedNodes(setRelayed bool, relay string, relayed []string) []models.Node {
relayHost, err = GetHost(relay.HostID)
if err != nil {
return
}
err = validateHostRelay(relay)
if err != nil {
return
}
relayHost.IsRelay = true
relayHost.ProxyEnabled = true
relayHost.RelayedHosts = relay.RelayedHosts
err = UpsertHost(relayHost)
if err != nil {
return
}
relayedHosts = SetRelayedHosts(true, relay.HostID, relay.RelayedHosts)
return
}
// SetRelayedHosts - updates the relayed hosts status
func SetRelayedHosts(setRelayed bool, relayHostID string, relayedHostIDs []string) []models.Host {
var relayedHosts []models.Host
for _, relayedHostID := range relayedHostIDs {
host, err := GetHost(relayedHostID)
if err == nil {
if setRelayed {
host.IsRelayed = true
host.RelayedBy = relayHostID
host.ProxyEnabled = true
} else {
host.IsRelayed = false
host.RelayedBy = ""
}
err = UpsertHost(host)
if err == nil {
relayedHosts = append(relayedHosts, *host)
}
}
}
return relayedHosts
}
// SetRelayedNodes- set relayed nodes
func SetRelayedNodes(setRelayed bool, networkName string, addrs []string) ([]models.Node, error) {
var returnnodes []models.Node var returnnodes []models.Node
networkNodes, err := GetNetworkNodes(networkName) for _, id := range relayed {
if err != nil { node, err := GetNodeByID(id)
return returnnodes, err if err != nil {
} logger.Log(0, "setRelayedNodes.GetNodebyID", err.Error())
for _, node := range networkNodes { continue
for _, addr := range addrs {
if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
if setRelayed {
node.IsRelayed = true
} else {
node.IsRelayed = false
}
data, err := json.Marshal(&node)
if err != nil {
return returnnodes, err
}
database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
returnnodes = append(returnnodes, node)
}
} }
} node.IsRelayed = setRelayed
return returnnodes, nil if node.IsRelayed {
} node.RelayedBy = relay
func GetRelayedNodes(relayNode *models.Node) ([]models.Node, error) { } else {
var returnnodes []models.Node node.RelayedBy = ""
networkNodes, err := GetNetworkNodes(relayNode.Network)
if err != nil {
return returnnodes, err
}
for _, node := range networkNodes {
for _, addr := range relayNode.RelayAddrs {
if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
returnnodes = append(returnnodes, node)
}
} }
node.SetLastModified()
data, err := json.Marshal(&node)
if err != nil {
logger.Log(0, "setRelayedNodes.Marshal", err.Error())
continue
}
if err := database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME); err != nil {
logger.Log(0, "setRelayedNodes.Insert", err.Error())
continue
}
returnnodes = append(returnnodes, node)
} }
return returnnodes, nil return returnnodes
} }
// GetRelayedHosts - gets the relayed hosts of a relay host //func GetRelayedNodes(relayNode *models.Node) (models.Node, error) {
func GetRelayedHosts(relayHost *models.Host) []models.Host { // var returnnodes []models.Node
relayedHosts := []models.Host{} // networkNodes, err := GetNetworkNodes(relayNode.Network)
// if err != nil {
for _, hostID := range relayHost.RelayedHosts { // return returnnodes, err
relayedHost, err := GetHost(hostID) // }
if err == nil { // for _, node := range networkNodes {
relayedHosts = append(relayedHosts, *relayedHost) // for _, addr := range relayNode.RelayAddrs {
} // if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
} // returnnodes = append(returnnodes, node)
return relayedHosts // }
} // }
// }
// return returnnodes, nil
//}
// ValidateRelay - checks if relay is valid // ValidateRelay - checks if relay is valid
func ValidateRelay(relay models.RelayRequest) error { func ValidateRelay(relay models.RelayRequest) error {
var err error var err error
//isIp := functions.IsIpCIDR(gateway.RangeString) //isIp := functions.IsIpCIDR(gateway.RangeString)
empty := len(relay.RelayAddrs) == 0 empty := len(relay.RelayedNodes) == 0
if empty { if empty {
err = errors.New("IP Ranges Cannot Be Empty") return errors.New("IP Ranges Cannot Be Empty")
}
node, err := GetNodeByID(relay.NodeID)
if err != nil {
return err
}
if node.IsRelay {
return errors.New("node is already acting as a relay")
}
for _, relayedNodeID := range relay.RelayedNodes {
relayedNode, err := GetNodeByID(relayedNodeID)
if err != nil {
return err
}
if relayedNode.IsIngressGateway {
return errors.New("cannot relay an ingress gateway (" + relayedNodeID + ")")
}
} }
return err return err
} }
func validateHostRelay(relay models.HostRelayRequest) error { // UpdateRelayed - updates relay nodes
if len(relay.RelayedHosts) == 0 { func UpdateRelayed(relay string, oldNodes []string, newNodes []string) []models.Node {
return errors.New("relayed hosts are empty") _ = SetRelayedNodes(false, relay, oldNodes)
} return SetRelayedNodes(true, relay, newNodes)
return nil
}
// UpdateRelay - updates a relay
func UpdateRelay(network string, oldAddrs []string, newAddrs []string) []models.Node {
var returnnodes []models.Node
time.Sleep(time.Second / 4)
_, err := SetRelayedNodes(false, network, oldAddrs)
if err != nil {
logger.Log(1, err.Error())
}
returnnodes, err = SetRelayedNodes(true, network, newAddrs)
if err != nil {
logger.Log(1, err.Error())
}
return returnnodes
} }
// DeleteRelay - deletes a relay // DeleteRelay - deletes a relay
@@ -188,15 +141,10 @@ func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) {
if err != nil { if err != nil {
return returnnodes, models.Node{}, err return returnnodes, models.Node{}, err
} }
returnnodes, err = SetRelayedNodes(false, node.Network, node.RelayAddrs) returnnodes = SetRelayedNodes(false, nodeid, node.RelayedNodes)
if err != nil {
return returnnodes, node, err
}
node.IsRelay = false node.IsRelay = false
node.RelayAddrs = []string{} node.RelayedNodes = []string{}
node.SetLastModified() node.SetLastModified()
data, err := json.Marshal(&node) data, err := json.Marshal(&node)
if err != nil { if err != nil {
return returnnodes, models.Node{}, err return returnnodes, models.Node{}, err
@@ -207,24 +155,20 @@ func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) {
return returnnodes, node, nil return returnnodes, node, nil
} }
// DeleteHostRelay - removes host as relay func getRelayedAddresses(id string) []net.IPNet {
func DeleteHostRelay(relayHostID string) (relayHost *models.Host, relayedHosts []models.Host, err error) { addrs := []net.IPNet{}
relayHost, err = GetHost(relayHostID) node, err := GetNodeByID(id)
if err != nil { if err != nil {
return logger.Log(0, "getRelayedAddresses: "+err.Error())
return addrs
} }
relayedHosts = SetRelayedHosts(false, relayHostID, relayHost.RelayedHosts) if node.Address.IP != nil {
relayHost.IsRelay = false node.Address.Mask = net.CIDRMask(32, 32)
relayHost.RelayedHosts = []string{} addrs = append(addrs, node.Address)
err = UpsertHost(relayHost)
if err != nil {
return
} }
return if node.Address6.IP != nil {
} node.Address.Mask = net.CIDRMask(128, 128)
addrs = append(addrs, node.Address6)
// UpdateHostRelay - updates the relay host with new relayed hosts }
func UpdateHostRelay(relayHostID string, oldRelayedHosts, newRelayedHosts []string) { return addrs
_ = SetRelayedHosts(false, relayHostID, oldRelayedHosts)
_ = SetRelayedHosts(true, relayHostID, newRelayedHosts)
} }

View File

@@ -4,17 +4,18 @@ import (
"encoding/json" "encoding/json"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/servercfg"
) )
var ( var (
// Node_Limit - dummy var for community
Node_Limit = 1000000000
// Networks_Limit - dummy var for community // Networks_Limit - dummy var for community
Networks_Limit = 1000000000 Networks_Limit = 1000000000
// Users_Limit - dummy var for community // Users_Limit - dummy var for community
Users_Limit = 1000000000 Users_Limit = 1000000000
// Clients_Limit - dummy var for community // Clients_Limit - dummy var for community
Clients_Limit = 1000000000 Clients_Limit = 1000000000
// Hosts_Limit - dummy var for community
Hosts_Limit = 1000000000
// Free_Tier - specifies if free tier // Free_Tier - specifies if free tier
Free_Tier = false Free_Tier = false
) )
@@ -85,3 +86,11 @@ func StoreJWTSecret(privateKey string) error {
} }
return database.Insert("nm-jwt-secret", string(data), database.SERVERCONF_TABLE_NAME) return database.Insert("nm-jwt-secret", string(data), database.SERVERCONF_TABLE_NAME)
} }
func SetFreeTierLimits() {
Free_Tier = true
Users_Limit = servercfg.GetUserLimit()
Clients_Limit = servercfg.GetClientLimit()
Networks_Limit = servercfg.GetNetworkLimit()
Hosts_Limit = servercfg.GetHostLimit()
}

View File

@@ -60,6 +60,7 @@ func sendTelemetry() error {
Event: "daily checkin", Event: "daily checkin",
Properties: posthog.NewProperties(). Properties: posthog.NewProperties().
Set("nodes", d.Nodes). Set("nodes", d.Nodes).
Set("hosts", d.Hosts).
Set("servers", d.Servers). Set("servers", d.Servers).
Set("non-server nodes", d.Count.NonServer). Set("non-server nodes", d.Count.NonServer).
Set("extclients", d.ExtClients). Set("extclients", d.ExtClients).
@@ -84,6 +85,7 @@ func fetchTelemetryData() (telemetryData, error) {
data.ExtClients = getDBLength(database.EXT_CLIENT_TABLE_NAME) data.ExtClients = getDBLength(database.EXT_CLIENT_TABLE_NAME)
data.Users = getDBLength(database.USERS_TABLE_NAME) data.Users = getDBLength(database.USERS_TABLE_NAME)
data.Networks = getDBLength(database.NETWORKS_TABLE_NAME) data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
data.Hosts = getDBLength(database.HOSTS_TABLE_NAME)
data.Version = servercfg.GetVersion() data.Version = servercfg.GetVersion()
data.Servers = getServerCount() data.Servers = getServerCount()
nodes, err := GetAllNodes() nodes, err := GetAllNodes()
@@ -167,6 +169,7 @@ func getDBLength(dbname string) int {
// telemetryData - What data to send to posthog // telemetryData - What data to send to posthog
type telemetryData struct { type telemetryData struct {
Nodes int Nodes int
Hosts int
ExtClients int ExtClients int
Users int Users int
Count clientCount Count clientCount

View File

@@ -1,10 +1,13 @@
package logic package logic
import ( import (
"context"
"fmt" "fmt"
"sync"
"time" "time"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
) )
// == Constants == // == Constants ==
@@ -12,6 +15,9 @@ import (
// How long to wait before sending telemetry to server (24 hours) // How long to wait before sending telemetry to server (24 hours)
const timer_hours_between_runs = 24 const timer_hours_between_runs = 24
// HookManagerCh - channel to add any new hooks
var HookManagerCh = make(chan models.HookDetails, 2)
// == Public == // == Public ==
// TimerCheckpoint - Checks if 24 hours has passed since telemetry was last sent. If so, sends telemetry data to posthog // TimerCheckpoint - Checks if 24 hours has passed since telemetry was last sent. If so, sends telemetry data to posthog
@@ -40,6 +46,36 @@ func AddHook(ifaceToAdd interface{}) {
timeHooks = append(timeHooks, ifaceToAdd) timeHooks = append(timeHooks, ifaceToAdd)
} }
// StartHookManager - listens on `HookManagerCh` to run any hook
func StartHookManager(ctx context.Context, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <-ctx.Done():
logger.Log(0, "## Stopping Hook Manager")
return
case newhook := <-HookManagerCh:
wg.Add(1)
go addHookWithInterval(ctx, wg, newhook.Hook, newhook.Interval)
}
}
}
func addHookWithInterval(ctx context.Context, wg *sync.WaitGroup, hook func() error, interval time.Duration) {
defer wg.Done()
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
hook()
}
}
}
// == private == // == private ==
// timeHooks - functions to run once a day, functions must take no parameters // timeHooks - functions to run once a day, functions must take no parameters

View File

@@ -26,6 +26,30 @@ func GetUser(username string) (*models.User, error) {
return &user, err return &user, err
} }
// GetReturnUser - gets a user
func GetReturnUser(username string) (models.ReturnUser, error) {
var user models.ReturnUser
record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
if err != nil {
return user, err
}
if err = json.Unmarshal([]byte(record), &user); err != nil {
return models.ReturnUser{}, err
}
return user, err
}
// ToReturnUser - gets a user as a return user
func ToReturnUser(user models.User) models.ReturnUser {
return models.ReturnUser{
UserName: user.UserName,
Networks: user.Networks,
IsAdmin: user.IsAdmin,
Groups: user.Groups,
}
}
// GetGroupUsers - gets users in a group // GetGroupUsers - gets users in a group
func GetGroupUsers(group string) ([]models.ReturnUser, error) { func GetGroupUsers(group string) ([]models.ReturnUser, error) {
var returnUsers []models.ReturnUser var returnUsers []models.ReturnUser

View File

@@ -29,11 +29,11 @@ func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
} }
} }
if newNode.IsRelay { if newNode.IsRelay {
if len(currentNode.RelayAddrs) != len(newNode.RelayAddrs) { if len(currentNode.RelayedNodes) != len(newNode.RelayedNodes) {
return true return true
} }
for _, address := range newNode.RelayAddrs { for _, node := range newNode.RelayedNodes {
if !StringSliceContains(currentNode.RelayAddrs, address) { if !StringSliceContains(currentNode.RelayedNodes, node) {
return true return true
} }
} }

View File

@@ -29,7 +29,7 @@ import (
"golang.org/x/exp/slog" "golang.org/x/exp/slog"
) )
var version = "v0.20.2" var version = "v0.20.3"
// Start DB Connection and start API Request Handler // Start DB Connection and start API Request Handler
func main() { func main() {
@@ -41,6 +41,9 @@ func main() {
initialize() // initial db and acls initialize() // initial db and acls
setGarbageCollection() setGarbageCollection()
setVerbosity() setVerbosity()
if servercfg.DeployedByOperator() && !servercfg.Is_EE {
logic.SetFreeTierLimits()
}
defer database.CloseDB() defer database.CloseDB()
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, os.Interrupt) ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, os.Interrupt)
defer stop() defer stop()
@@ -88,7 +91,6 @@ func initialize() { // Client Mode Prereq Check
if err != nil { if err != nil {
logger.Log(1, "Timer error occurred: ", err.Error()) logger.Log(1, "Timer error occurred: ", err.Error())
} }
logic.EnterpriseCheck() logic.EnterpriseCheck()
var authProvider = auth.InitializeAuthProvider() var authProvider = auth.InitializeAuthProvider()
@@ -146,6 +148,8 @@ func startControllers(wg *sync.WaitGroup, ctx context.Context) {
logger.Log(0, "No Server Mode selected, so nothing is being served! Set Rest mode (REST_BACKEND) or MessageQueue (MESSAGEQUEUE_BACKEND) to 'true'.") logger.Log(0, "No Server Mode selected, so nothing is being served! Set Rest mode (REST_BACKEND) or MessageQueue (MESSAGEQUEUE_BACKEND) to 'true'.")
} }
wg.Add(1)
go logic.StartHookManager(ctx, wg)
} }
// Should we be using a context vice a waitgroup???????????? // Should we be using a context vice a waitgroup????????????

View File

@@ -34,6 +34,7 @@ type ApiHost struct {
RelayedBy string `json:"relayed_by" bson:"relayed_by" yaml:"relayed_by"` RelayedBy string `json:"relayed_by" bson:"relayed_by" yaml:"relayed_by"`
IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"` IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
RelayedHosts []string `json:"relay_hosts" bson:"relay_hosts" yaml:"relay_hosts"` RelayedHosts []string `json:"relay_hosts" bson:"relay_hosts" yaml:"relay_hosts"`
NatType string `json:"nat_type" yaml:"nat_type"`
} }
// Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host // Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host
@@ -67,10 +68,7 @@ func (h *Host) ConvertNMHostToAPI() *ApiHost {
a.Verbosity = h.Verbosity a.Verbosity = h.Verbosity
a.Version = h.Version a.Version = h.Version
a.IsDefault = h.IsDefault a.IsDefault = h.IsDefault
a.IsRelay = h.IsRelay a.NatType = h.NatType
a.RelayedHosts = h.RelayedHosts
a.IsRelayed = h.IsRelayed
a.RelayedBy = h.RelayedBy
return &a return &a
} }
@@ -108,10 +106,6 @@ func (a *ApiHost) ConvertAPIHostToNMHost(currentHost *Host) *Host {
h.Nodes = currentHost.Nodes h.Nodes = currentHost.Nodes
h.TrafficKeyPublic = currentHost.TrafficKeyPublic h.TrafficKeyPublic = currentHost.TrafficKeyPublic
h.OS = currentHost.OS h.OS = currentHost.OS
h.RelayedBy = a.RelayedBy
h.RelayedHosts = a.RelayedHosts
h.IsRelay = a.IsRelay
h.IsRelayed = a.IsRelayed
h.ProxyEnabled = a.ProxyEnabled h.ProxyEnabled = a.ProxyEnabled
h.IsDefault = a.IsDefault h.IsDefault = a.IsDefault
h.NatType = currentHost.NatType h.NatType = currentHost.NatType

View File

@@ -25,11 +25,12 @@ type ApiNode struct {
NetworkRange6 string `json:"networkrange6"` NetworkRange6 string `json:"networkrange6"`
IsRelayed bool `json:"isrelayed"` IsRelayed bool `json:"isrelayed"`
IsRelay bool `json:"isrelay"` IsRelay bool `json:"isrelay"`
RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"`
IsEgressGateway bool `json:"isegressgateway"` IsEgressGateway bool `json:"isegressgateway"`
IsIngressGateway bool `json:"isingressgateway"` IsIngressGateway bool `json:"isingressgateway"`
EgressGatewayRanges []string `json:"egressgatewayranges"` EgressGatewayRanges []string `json:"egressgatewayranges"`
EgressGatewayNatEnabled bool `json:"egressgatewaynatenabled"` EgressGatewayNatEnabled bool `json:"egressgatewaynatenabled"`
RelayAddrs []string `json:"relayaddrs"`
FailoverNode string `json:"failovernode"` FailoverNode string `json:"failovernode"`
DNSOn bool `json:"dnson"` DNSOn bool `json:"dnson"`
IngressDns string `json:"ingressdns"` IngressDns string `json:"ingressdns"`
@@ -53,6 +54,8 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
convertedNode.HostID, _ = uuid.Parse(a.HostID) convertedNode.HostID, _ = uuid.Parse(a.HostID)
convertedNode.IsRelay = a.IsRelay convertedNode.IsRelay = a.IsRelay
convertedNode.IsRelayed = a.IsRelayed convertedNode.IsRelayed = a.IsRelayed
convertedNode.RelayedBy = a.RelayedBy
convertedNode.RelayedNodes = a.RelayedNodes
convertedNode.PendingDelete = a.PendingDelete convertedNode.PendingDelete = a.PendingDelete
convertedNode.Failover = a.Failover convertedNode.Failover = a.Failover
convertedNode.IsEgressGateway = a.IsEgressGateway convertedNode.IsEgressGateway = a.IsEgressGateway
@@ -66,7 +69,7 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest
convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled
convertedNode.PersistentKeepalive = time.Second * time.Duration(a.PersistentKeepalive) convertedNode.PersistentKeepalive = time.Second * time.Duration(a.PersistentKeepalive)
convertedNode.RelayAddrs = a.RelayAddrs convertedNode.RelayedNodes = a.RelayedNodes
convertedNode.DefaultACL = a.DefaultACL convertedNode.DefaultACL = a.DefaultACL
convertedNode.OwnerID = currentNode.OwnerID convertedNode.OwnerID = currentNode.OwnerID
_, networkRange, err := net.ParseCIDR(a.NetworkRange) _, networkRange, err := net.ParseCIDR(a.NetworkRange)
@@ -140,11 +143,12 @@ func (nm *Node) ConvertToAPINode() *ApiNode {
} }
apiNode.IsRelayed = nm.IsRelayed apiNode.IsRelayed = nm.IsRelayed
apiNode.IsRelay = nm.IsRelay apiNode.IsRelay = nm.IsRelay
apiNode.RelayedBy = nm.RelayedBy
apiNode.RelayedNodes = nm.RelayedNodes
apiNode.IsEgressGateway = nm.IsEgressGateway apiNode.IsEgressGateway = nm.IsEgressGateway
apiNode.IsIngressGateway = nm.IsIngressGateway apiNode.IsIngressGateway = nm.IsIngressGateway
apiNode.EgressGatewayRanges = nm.EgressGatewayRanges apiNode.EgressGatewayRanges = nm.EgressGatewayRanges
apiNode.EgressGatewayNatEnabled = nm.EgressGatewayNatEnabled apiNode.EgressGatewayNatEnabled = nm.EgressGatewayNatEnabled
apiNode.RelayAddrs = nm.RelayAddrs
apiNode.FailoverNode = nm.FailoverNode.String() apiNode.FailoverNode = nm.FailoverNode.String()
if isUUIDSet(apiNode.FailoverNode) { if isUUIDSet(apiNode.FailoverNode) {
apiNode.FailoverNode = "" apiNode.FailoverNode = ""

View File

@@ -8,18 +8,20 @@ import (
// HostPeerUpdate - struct for host peer updates // HostPeerUpdate - struct for host peer updates
type HostPeerUpdate struct { type HostPeerUpdate struct {
Host Host `json:"host" bson:"host" yaml:"host"` Host Host `json:"host" bson:"host" yaml:"host"`
Server string `json:"server" bson:"server" yaml:"server"` NodeAddrs []net.IPNet `json:"nodes_addrs" yaml:"nodes_addrs"`
ServerVersion string `json:"serverversion" bson:"serverversion" yaml:"serverversion"` Server string `json:"server" bson:"server" yaml:"server"`
ServerAddrs []ServerAddr `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"` ServerVersion string `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
NodePeers []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"` ServerAddrs []ServerAddr `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
Peers []wgtypes.PeerConfig NodePeers []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
HostPeerIDs HostPeerMap `json:"hostpeerids" bson:"hostpeerids" yaml:"hostpeerids"` Peers []wgtypes.PeerConfig
ProxyUpdate ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"` HostPeerIDs HostPeerMap `json:"hostpeerids" bson:"hostpeerids" yaml:"hostpeerids"`
EgressInfo map[string]EgressInfo `json:"egress_info" bson:"egress_info" yaml:"egress_info"` // map key is node ID ProxyUpdate ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
IngressInfo IngressInfo `json:"ingress_info" bson:"ext_peers" yaml:"ext_peers"` EgressInfo map[string]EgressInfo `json:"egress_info" bson:"egress_info" yaml:"egress_info"` // map key is node ID
PeerIDs PeerMap `json:"peerids" bson:"peerids" yaml:"peerids"` IngressInfo IngressInfo `json:"ingress_info" bson:"ext_peers" yaml:"ext_peers"`
HostNetworkInfo HostInfoMap `json:"host_network_info,omitempty" bson:"host_network_info,omitempty" yaml:"host_network_info,omitempty"` PeerIDs PeerMap `json:"peerids" bson:"peerids" yaml:"peerids"`
EndpointDetection bool `json:"endpointdetection" yaml:"endpointdetection"`
HostNetworkInfo HostInfoMap `json:"host_network_info,omitempty" bson:"host_network_info,omitempty" yaml:"host_network_info,omitempty"`
} }
// IngressInfo - struct for ingress info // IngressInfo - struct for ingress info

View File

@@ -69,6 +69,10 @@ type CommonNode struct {
IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"` IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"`
EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"` EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"` IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"`
IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"`
IngressDNS string `json:"ingressdns" yaml:"ingressdns"` IngressDNS string `json:"ingressdns" yaml:"ingressdns"`
DNSOn bool `json:"dnson" yaml:"dnson"` DNSOn bool `json:"dnson" yaml:"dnson"`
PersistentKeepalive time.Duration `json:"persistentkeepalive" yaml:"persistentkeepalive"` PersistentKeepalive time.Duration `json:"persistentkeepalive" yaml:"persistentkeepalive"`
@@ -86,9 +90,6 @@ type Node struct {
EgressGatewayRequest EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"` EgressGatewayRequest EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"`
IngressGatewayRange string `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"` IngressGatewayRange string `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"`
IngressGatewayRange6 string `json:"ingressgatewayrange6" bson:"ingressgatewayrange6" yaml:"ingressgatewayrange6"` IngressGatewayRange6 string `json:"ingressgatewayrange6" bson:"ingressgatewayrange6" yaml:"ingressgatewayrange6"`
IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"`
// == PRO == // == PRO ==
DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"` DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"` OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
@@ -180,6 +181,14 @@ func isLess(ipA string, ipB string) bool {
return bytes.Compare(ipNetA, ipNetB) < 0 return bytes.Compare(ipNetA, ipNetB) < 0
} }
// Node.PrimaryAddress - return ipv4 address if present, else return ipv6
func (node *Node) PrimaryAddressIPNet() net.IPNet {
if node.Address.IP != nil {
return node.Address
}
return node.Address6
}
// Node.PrimaryAddress - return ipv4 address if present, else return ipv6 // Node.PrimaryAddress - return ipv4 address if present, else return ipv6
func (node *Node) PrimaryAddress() string { func (node *Node) PrimaryAddress() string {
if node.Address.IP != nil { if node.Address.IP != nil {
@@ -350,7 +359,7 @@ func (node *LegacyNode) SetDefaultFailover() {
} }
// Node.Fill - fills other node data into calling node data if not set on calling node // Node.Fill - fills other node data into calling node data if not set on calling node
func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftables present func (newNode *Node) Fill(currentNode *Node, isEE bool) { // TODO add new field for nftables present
newNode.ID = currentNode.ID newNode.ID = currentNode.ID
newNode.HostID = currentNode.HostID newNode.HostID = currentNode.HostID
// Revisit the logic for boolean values // Revisit the logic for boolean values
@@ -401,13 +410,13 @@ func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftable
if newNode.Action == "" { if newNode.Action == "" {
newNode.Action = currentNode.Action newNode.Action = currentNode.Action
} }
if newNode.RelayAddrs == nil { if newNode.RelayedNodes == nil {
newNode.RelayAddrs = currentNode.RelayAddrs newNode.RelayedNodes = currentNode.RelayedNodes
} }
if newNode.IsRelay != currentNode.IsRelay { if newNode.IsRelay != currentNode.IsRelay && isEE {
newNode.IsRelay = currentNode.IsRelay newNode.IsRelay = currentNode.IsRelay
} }
if newNode.IsRelayed == currentNode.IsRelayed { if newNode.IsRelayed == currentNode.IsRelayed && isEE {
newNode.IsRelayed = currentNode.IsRelayed newNode.IsRelayed = currentNode.IsRelayed
} }
if newNode.Server == "" { if newNode.Server == "" {

View File

@@ -2,6 +2,7 @@ package models
import ( import (
"strings" "strings"
"time"
jwt "github.com/golang-jwt/jwt/v4" jwt "github.com/golang-jwt/jwt/v4"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -153,9 +154,9 @@ type EgressGatewayRequest struct {
// RelayRequest - relay request struct // RelayRequest - relay request struct
type RelayRequest struct { type RelayRequest struct {
NodeID string `json:"nodeid" bson:"nodeid"` NodeID string `json:"nodeid"`
NetID string `json:"netid" bson:"netid"` NetID string `json:"netid"`
RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs"` RelayedNodes []string `json:"relayaddrs"`
} }
// HostRelayRequest - struct for host relay creation // HostRelayRequest - struct for host relay creation
@@ -274,3 +275,18 @@ type StunServer struct {
Domain string `json:"domain" yaml:"domain"` Domain string `json:"domain" yaml:"domain"`
Port int `json:"port" yaml:"port"` Port int `json:"port" yaml:"port"`
} }
// HookDetails - struct to hold hook info
type HookDetails struct {
Hook func() error
Interval time.Duration
}
// LicenseLimits - struct license limits
type LicenseLimits struct {
Servers int `json:"servers"`
Users int `json:"users"`
Hosts int `json:"hosts"`
Clients int `json:"clients"`
Networks int `json:"networks"`
}

View File

@@ -6,11 +6,14 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"strings"
"sync" "sync"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
) )
const already_exists = "ALREADY_EXISTS"
type ( type (
emqxUser struct { emqxUser struct {
UserID string `json:"user_id"` UserID string `json:"user_id"`
@@ -99,7 +102,9 @@ func CreateEmqxUser(username, password string, admin bool) error {
if err != nil { if err != nil {
return err return err
} }
return fmt.Errorf("error creating EMQX user %v", string(msg)) if !strings.Contains(string(msg), already_exists) {
return fmt.Errorf("error creating EMQX user %v", string(msg))
}
} }
return nil return nil
} }

View File

@@ -240,7 +240,7 @@ func UpdateMetrics(client mqtt.Client, msg mqtt.Message) {
} }
} }
} }
slog.Info("updated node metrics", "id", id) slog.Debug("updated node metrics", "id", id)
} }
} }

View File

@@ -88,18 +88,20 @@ func PublishSingleHostPeerUpdate(ctx context.Context, host *models.Host, deleted
if len(peerUpdate.Peers) == 0 { // no peers to send if len(peerUpdate.Peers) == 0 { // no peers to send
return nil return nil
} }
proxyUpdate, err := logic.GetProxyUpdateForHost(ctx, host) if host.OS != models.OS_Types.IoT {
if err != nil { proxyUpdate, err := logic.GetProxyUpdateForHost(ctx, host)
return err if err != nil {
} return err
proxyUpdate.Server = servercfg.GetServer() }
if host.ProxyEnabled { proxyUpdate.Server = servercfg.GetServer()
proxyUpdate.Action = models.ProxyUpdate if host.ProxyEnabled {
} else { proxyUpdate.Action = models.ProxyUpdate
proxyUpdate.Action = models.NoProxy } else {
} proxyUpdate.Action = models.NoProxy
}
peerUpdate.ProxyUpdate = proxyUpdate peerUpdate.ProxyUpdate = proxyUpdate
}
data, err := json.Marshal(&peerUpdate) data, err := json.Marshal(&peerUpdate)
if err != nil { if err != nil {

View File

@@ -1,5 +1,5 @@
# Netmaker v0.20.2 # Netmaker v0.20.3
## whats new ## whats new
- -

View File

@@ -19,6 +19,7 @@ NETMAKER_ACCOUNT_ID=
LICENSE_KEY= LICENSE_KEY=
SERVER_IMAGE_TAG= SERVER_IMAGE_TAG=
UI_IMAGE_TAG= UI_IMAGE_TAG=
NETCLIENT_ENDPOINT_DETECTION="disabled"
# used for HA - identifies this server vs other servers # used for HA - identifies this server vs other servers
NODE_ID="netmaker-server-1" NODE_ID="netmaker-server-1"
METRICS_EXPORTER="off" METRICS_EXPORTER="off"

View File

@@ -4,7 +4,7 @@ CONFIG_FILE=netmaker.env
# location of nm-quick.sh (usually `/root`) # location of nm-quick.sh (usually `/root`)
SCRIPT_DIR=$(dirname "$(realpath "$0")") SCRIPT_DIR=$(dirname "$(realpath "$0")")
CONFIG_PATH="$SCRIPT_DIR/$CONFIG_FILE" CONFIG_PATH="$SCRIPT_DIR/$CONFIG_FILE"
NM_QUICK_VERSION="0.1.0" NM_QUICK_VERSION="0.1.1"
LATEST=$(curl -s https://api.github.com/repos/gravitl/netmaker/releases/latest | grep "tag_name" | cut -d : -f 2,3 | tr -d [:space:],\") LATEST=$(curl -s https://api.github.com/repos/gravitl/netmaker/releases/latest | grep "tag_name" | cut -d : -f 2,3 | tr -d [:space:],\")
if [ $(id -u) -ne 0 ]; then if [ $(id -u) -ne 0 ]; then
@@ -17,11 +17,12 @@ unset BUILD_TYPE
unset BUILD_TAG unset BUILD_TAG
unset IMAGE_TAG unset IMAGE_TAG
unset AUTO_BUILD unset AUTO_BUILD
unset NETMAKER_BASE_DOMAIN
# usage - displays usage instructions # usage - displays usage instructions
usage() { usage() {
echo "nm-quick.sh v$NM_QUICK_VERSION" echo "nm-quick.sh v$NM_QUICK_VERSION"
echo "usage: ./nm-quick.sh [-e] [-b buildtype] [-t tag] [-a auto]" echo "usage: ./nm-quick.sh [-e] [-b buildtype] [-t tag] [-a auto] [-d domain]"
echo " -e if specified, will install netmaker EE" echo " -e if specified, will install netmaker EE"
echo " -b type of build; options:" echo " -b type of build; options:"
echo " \"version\" - will install a specific version of Netmaker using remote git and dockerhub" echo " \"version\" - will install a specific version of Netmaker using remote git and dockerhub"
@@ -29,14 +30,16 @@ usage() {
echo " \"branch\": - will install a specific branch using remote git and dockerhub" echo " \"branch\": - will install a specific branch using remote git and dockerhub"
echo " -t tag of build; if buildtype=version, tag=version. If builtype=branch or builtype=local, tag=branch" echo " -t tag of build; if buildtype=version, tag=version. If builtype=branch or builtype=local, tag=branch"
echo " -a auto-build; skip prompts and use defaults, if none provided" echo " -a auto-build; skip prompts and use defaults, if none provided"
echo " -d domain; if specified, will use this domain instead of auto-generating one"
echo "examples:" echo "examples:"
echo " nm-quick.sh -e -b version -t $LATEST" echo " nm-quick.sh -e -b version -t $LATEST"
echo " nm-quick.sh -e -b local -t feature_v0.17.2_newfeature" echo " nm-quick.sh -e -b local -t feature_v0.17.2_newfeature"
echo " nm-quick.sh -e -b branch -t develop" echo " nm-quick.sh -e -b branch -t develop"
echo " nm-quick.sh -e -b version -t $LATEST -a -d example.com"
exit 1 exit 1
} }
while getopts evab:t: flag; do while getopts evab:d:t: flag; do
case "${flag}" in case "${flag}" in
e) e)
INSTALL_TYPE="ee" INSTALL_TYPE="ee"
@@ -60,6 +63,9 @@ while getopts evab:t: flag; do
t) t)
BUILD_TAG=${OPTARG} BUILD_TAG=${OPTARG}
;; ;;
d)
NETMAKER_BASE_DOMAIN=${OPTARG}
;;
esac esac
done done
@@ -303,9 +309,9 @@ save_config() { (
local toCopy=("SERVER_HOST" "MASTER_KEY" "TURN_USERNAME" "TURN_PASSWORD" "MQ_USERNAME" "MQ_PASSWORD" local toCopy=("SERVER_HOST" "MASTER_KEY" "TURN_USERNAME" "TURN_PASSWORD" "MQ_USERNAME" "MQ_PASSWORD"
"INSTALL_TYPE" "NODE_ID" "METRICS_EXPORTER" "PROMETHEUS" "DNS_MODE" "NETCLIENT_AUTO_UPDATE" "API_PORT" "INSTALL_TYPE" "NODE_ID" "METRICS_EXPORTER" "PROMETHEUS" "DNS_MODE" "NETCLIENT_AUTO_UPDATE" "API_PORT"
"CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "STUN_PORT" "VERBOSITY" "CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "STUN_PORT" "VERBOSITY"
"DEFAULT_PROXY_MODE" "TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND" "DISABLE_REMOTE_IP_CHECK" "DEFAULT_PROXY_MODE" "TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND"
"TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET" "FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "DISABLE_REMOTE_IP_CHECK" "NETCLIENT_ENDPOINT_DETECTION" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
"EXPORTER_API_PORT") "FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT")
for name in "${toCopy[@]}"; do for name in "${toCopy[@]}"; do
save_config_item $name "${!name}" save_config_item $name "${!name}"
done done
@@ -490,8 +496,9 @@ set_install_vars() {
if [ "$IP_ADDR" = "" ]; then if [ "$IP_ADDR" = "" ]; then
IP_ADDR=$(curl -s ifconfig.me) IP_ADDR=$(curl -s ifconfig.me)
fi fi
if [ "$NETMAKER_BASE_DOMAIN" = "" ]; then
NETMAKER_BASE_DOMAIN=nm.$(echo $IP_ADDR | tr . -).nip.io NETMAKER_BASE_DOMAIN=nm.$(echo $IP_ADDR | tr . -).nip.io
fi
SERVER_HOST=$IP_ADDR SERVER_HOST=$IP_ADDR
if test -z "$MASTER_KEY"; then if test -z "$MASTER_KEY"; then
MASTER_KEY=$( MASTER_KEY=$(

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
LATEST="v0.20.2" LATEST="v0.20.3"
INSTALL_PATH="/root" INSTALL_PATH="/root"
trap restore_old_netmaker_instructions trap restore_old_netmaker_instructions

View File

@@ -10,6 +10,7 @@ import (
"time" "time"
"github.com/gravitl/netmaker/config" "github.com/gravitl/netmaker/config"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
) )
@@ -51,6 +52,11 @@ func GetServerConfig() config.ServerConfig {
} else { } else {
cfg.NetclientAutoUpdate = "disabled" cfg.NetclientAutoUpdate = "disabled"
} }
if EndpointDetectionEnabled() {
cfg.NetclientEndpointDetection = "enabled"
} else {
cfg.NetclientEndpointDetection = "disabled"
}
if IsRestBackend() { if IsRestBackend() {
cfg.RestBackend = "on" cfg.RestBackend = "on"
} }
@@ -432,6 +438,17 @@ func AutoUpdateEnabled() bool {
return true return true
} }
// EndpointDetectionEnabled returns a boolean indicating whether netclient endpoint detection is enabled or disabled
// default is enabled
func EndpointDetectionEnabled() bool {
if os.Getenv("NETCLIENT_ENDPOINT_DETECTION") == "disabled" {
return false
} else if config.Config.Server.NetclientEndpointDetection == "disabled" {
return false
}
return true
}
// IsDNSMode - should it run with DNS // IsDNSMode - should it run with DNS
func IsDNSMode() bool { func IsDNSMode() bool {
isdns := true isdns := true
@@ -725,6 +742,58 @@ func IsProxyEnabled() bool {
return enabled return enabled
} }
// GetNetworkLimit - fetches free tier limits on users
func GetUserLimit() int {
var userslimit int
if os.Getenv("USERS_LIMIT") != "" {
userslimit, _ = strconv.Atoi(os.Getenv("USERS_LIMIT"))
} else {
userslimit = config.Config.Server.UsersLimit
}
return userslimit
}
// GetNetworkLimit - fetches free tier limits on networks
func GetNetworkLimit() int {
var networkslimit int
if os.Getenv("NETWORKS_LIMIT") != "" {
networkslimit, _ = strconv.Atoi(os.Getenv("NETWORKS_LIMIT"))
} else {
networkslimit = config.Config.Server.NetworksLimit
}
return networkslimit
}
// GetClientLimit - fetches free tier limits on ext. clients
func GetClientLimit() int {
var clientsLimit int
if os.Getenv("CLIENTS_LIMIT") != "" {
clientsLimit, _ = strconv.Atoi(os.Getenv("CLIENTS_LIMIT"))
} else {
clientsLimit = config.Config.Server.ClientsLimit
}
return clientsLimit
}
// GetHostLimit - fetches free tier limits on hosts
func GetHostLimit() int {
var hostsLimit int
if os.Getenv("HOSTS_LIMIT") != "" {
hostsLimit, _ = strconv.Atoi(os.Getenv("HOSTS_LIMIT"))
} else {
hostsLimit = config.Config.Server.HostsLimit
}
return hostsLimit
}
// DeployedByOperator - returns true if the instance is deployed by netmaker operator
func DeployedByOperator() bool {
if os.Getenv("DEPLOYED_BY_OPERATOR") != "" {
return os.Getenv("DEPLOYED_BY_OPERATOR") == "true"
}
return config.Config.Server.DeployedByOperator
}
// GetDefaultProxyMode - default proxy mode for a server // GetDefaultProxyMode - default proxy mode for a server
func GetDefaultProxyMode() config.ProxyMode { func GetDefaultProxyMode() config.ProxyMode {
var ( var (

View File

@@ -704,7 +704,7 @@ info:
API calls must be authenticated via a header of the format -H “Authorization: Bearer <YOUR_SECRET_KEY>” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes/<network>/authenticate endpoint, as documented below. API calls must be authenticated via a header of the format -H “Authorization: Bearer <YOUR_SECRET_KEY>” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes/<network>/authenticate endpoint, as documented below.
title: Netmaker title: Netmaker
version: 0.20.2 version: 0.20.3
paths: paths:
/api/dns: /api/dns:
get: get: