mirror of
https://github.com/gravitl/netmaker.git
synced 2025-11-02 13:04:11 +08:00
Merge branch 'develop' into remove-stun
This commit is contained in:
1
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -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
|
||||||
|
|||||||
57
.github/workflows/branchtest.yml
vendored
57
.github/workflows/branchtest.yml
vendored
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
28
.github/workflows/deletedroplets.yml
vendored
28
.github/workflows/deletedroplets.yml
vendored
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
22
cli/cmd/node/create_relay.go
Normal file
22
cli/cmd/node/create_relay.go
Normal 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)
|
||||||
|
}
|
||||||
@@ -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]))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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 ?")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
106
config/config.go
106
config/config.go
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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"))
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(¤tNode)
|
newNode := newData.ConvertToServerNode(¤tNode)
|
||||||
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(¤tNode, newNode)
|
ifaceDelta := logic.IfaceDelta(¤tNode, 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(¤tNode, 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(¤tNode, newNode, host); err != nil {
|
if err := mq.PublishReplaceDNS(¤tNode, 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 {
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
107
ee/ee_controllers/relay.go
Normal 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)
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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{}
|
||||||
|
|||||||
58
ee/types.go
58
ee/types.go
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
10
go.mod
@@ -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
20
go.sum
@@ -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=
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
259
logic/peers.go
259
logic/peers.go
@@ -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)
|
||||||
|
|||||||
234
logic/relay.go
234
logic/relay.go
@@ -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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
main.go
8
main.go
@@ -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????????????
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 = ""
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 == "" {
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
# Netmaker v0.20.2
|
# Netmaker v0.20.3
|
||||||
|
|
||||||
## whats new
|
## whats new
|
||||||
-
|
-
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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=$(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user