mirror of
https://github.com/gravitl/netmaker.git
synced 2024-09-20 07:16:06 +08:00
commit
4d062b88ba
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.16.1
|
||||||
- v0.16.0
|
- v0.16.0
|
||||||
- v0.15.2
|
- v0.15.2
|
||||||
- v0.15.1
|
- v0.15.1
|
||||||
|
|
2
.github/workflows/buildandrelease.yml
vendored
2
.github/workflows/buildandrelease.yml
vendored
|
@ -402,7 +402,7 @@ jobs:
|
||||||
cd netclient
|
cd netclient
|
||||||
env GOOS=darwin GOARCH=amd64 go build -tags=gui -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin/netclient .
|
env GOOS=darwin GOARCH=amd64 go build -tags=gui -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin/netclient .
|
||||||
env CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -tags=gui -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin-arm64/netclient main.go
|
env CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -tags=gui -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin-arm64/netclient main.go
|
||||||
env GOOS=darwin GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin/netclient-darwin-headless .
|
env GOOS=darwin GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin-headless/netclient .
|
||||||
- name: Upload darwin-amd64 to Release
|
- name: Upload darwin-amd64 to Release
|
||||||
uses: svenstaro/upload-release-action@v2
|
uses: svenstaro/upload-release-action@v2
|
||||||
with:
|
with:
|
||||||
|
|
2
.github/workflows/docker-builder.yml
vendored
2
.github/workflows/docker-builder.yml
vendored
|
@ -23,6 +23,6 @@ jobs:
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
platforms: linux/amd64, linux/arm64
|
platforms: linux/amd64, linux/arm64, linux/armv7l
|
||||||
file: ./docker/Dockerfile-go-builder
|
file: ./docker/Dockerfile-go-builder
|
||||||
tags: gravitl/go-builder:latest
|
tags: gravitl/go-builder:latest
|
||||||
|
|
21
.github/workflows/publish-docker.yml
vendored
21
.github/workflows/publish-docker.yml
vendored
|
@ -55,7 +55,7 @@ jobs:
|
||||||
sleep 10
|
sleep 10
|
||||||
kill %1
|
kill %1
|
||||||
-
|
-
|
||||||
name: Build arm and export to Docker
|
name: Build arm64 and export to Docker
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
@ -64,7 +64,22 @@ jobs:
|
||||||
tags: ${{ env.TAG }}
|
tags: ${{ env.TAG }}
|
||||||
build-args: version=${{ env.TAG }}
|
build-args: version=${{ env.TAG }}
|
||||||
-
|
-
|
||||||
name: Test arm
|
name: Test arm64
|
||||||
|
run: |
|
||||||
|
docker run --rm ${{ env.TAG }}&
|
||||||
|
sleep 10
|
||||||
|
kill %1
|
||||||
|
-
|
||||||
|
name: Build armv7l and export to Docker
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
load: true
|
||||||
|
platforms: linux/armv7l
|
||||||
|
tags: ${{ env.TAG }}
|
||||||
|
build-args: version=${{ env.TAG }}
|
||||||
|
-
|
||||||
|
name: Test armv7l
|
||||||
run: |
|
run: |
|
||||||
docker run --rm ${{ env.TAG }}&
|
docker run --rm ${{ env.TAG }}&
|
||||||
sleep 10
|
sleep 10
|
||||||
|
@ -74,7 +89,7 @@ jobs:
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64, linux/arm64
|
platforms: linux/amd64, linux/arm64, linux/armv7l
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ github.repository }}:${{ env.TAG }}, ${{ github.repository }}:latest
|
tags: ${{ github.repository }}:${{ env.TAG }}, ${{ github.repository }}:latest
|
||||||
build-args: version=${{ env.TAG }}
|
build-args: version=${{ env.TAG }}
|
||||||
|
|
|
@ -56,7 +56,7 @@ jobs:
|
||||||
sleep 10
|
sleep 10
|
||||||
kill %1
|
kill %1
|
||||||
-
|
-
|
||||||
name: Build arm and export to Docker
|
name: Build arm64 and export to Docker
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
@ -66,7 +66,7 @@ jobs:
|
||||||
tags: ${{ env.TAG }}
|
tags: ${{ env.TAG }}
|
||||||
build-args: version=${{ env.TAG }}
|
build-args: version=${{ env.TAG }}
|
||||||
-
|
-
|
||||||
name: Test arm
|
name: Test arm64
|
||||||
run: |
|
run: |
|
||||||
docker run --rm ${{ env.TAG }}&
|
docker run --rm ${{ env.TAG }}&
|
||||||
sleep 10
|
sleep 10
|
||||||
|
|
1
.github/workflows/test.yml
vendored
1
.github/workflows/test.yml
vendored
|
@ -17,6 +17,7 @@ jobs:
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build main.go
|
env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build main.go
|
||||||
|
env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags=ee main.go
|
||||||
cd netclient
|
cd netclient
|
||||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
|
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
|
||||||
env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build main.go
|
env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build main.go
|
||||||
|
|
|
@ -17,7 +17,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.16.0-informational?style=flat-square" />
|
<img src="https://img.shields.io/badge/Version-0.16.1-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" />
|
||||||
|
@ -99,6 +99,8 @@ After installing Netmaker, check out the [Walkthrough](https://itnext.io/getting
|
||||||
|
|
||||||
- [Netmaker K3S](https://github.com/geragcp/netmaker-k3s)
|
- [Netmaker K3S](https://github.com/geragcp/netmaker-k3s)
|
||||||
|
|
||||||
|
- [Run Netmaker + Netclient with Podman](https://github.com/agorgl/nm-setup)
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
[WireGuard](https://wireguard.com/) is a registered trademark of Jason A. Donenfeld.
|
[WireGuard](https://wireguard.com/) is a registered trademark of Jason A. Donenfeld.
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ version: "3.4"
|
||||||
services:
|
services:
|
||||||
netmaker:
|
netmaker:
|
||||||
container_name: netmaker
|
container_name: netmaker
|
||||||
image: gravitl/netmaker:v0.16.0-ee
|
image: gravitl/netmaker:v0.16.1-ee
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
- NET_RAW
|
- NET_RAW
|
||||||
|
@ -17,7 +17,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- dnsconfig:/root/config/dnsconfig
|
- dnsconfig:/root/config/dnsconfig
|
||||||
- sqldata:/root/data
|
- sqldata:/root/data
|
||||||
- shared_certs:/etc/netmaker
|
- mosquitto_data:/etc/netmaker
|
||||||
environment:
|
environment:
|
||||||
SERVER_NAME: "broker.NETMAKER_BASE_DOMAIN"
|
SERVER_NAME: "broker.NETMAKER_BASE_DOMAIN"
|
||||||
SERVER_HOST: "SERVER_PUBLIC_IP"
|
SERVER_HOST: "SERVER_PUBLIC_IP"
|
||||||
|
@ -42,6 +42,7 @@ services:
|
||||||
METRICS_EXPORTER: "on"
|
METRICS_EXPORTER: "on"
|
||||||
LICENSE_KEY: "YOUR_LICENSE_KEY"
|
LICENSE_KEY: "YOUR_LICENSE_KEY"
|
||||||
NETMAKER_ACCOUNT_ID: "YOUR_ACCOUNT_ID"
|
NETMAKER_ACCOUNT_ID: "YOUR_ACCOUNT_ID"
|
||||||
|
MQ_ADMIN_PASSWORD: "REPLACE_MQ_ADMIN_PASSWORD"
|
||||||
ports:
|
ports:
|
||||||
- "51821-51830:51821-51830/udp"
|
- "51821-51830:51821-51830/udp"
|
||||||
expose:
|
expose:
|
||||||
|
@ -54,7 +55,7 @@ services:
|
||||||
- traefik.http.services.netmaker-api.loadbalancer.server.port=8081
|
- traefik.http.services.netmaker-api.loadbalancer.server.port=8081
|
||||||
netmaker-ui:
|
netmaker-ui:
|
||||||
container_name: netmaker-ui
|
container_name: netmaker-ui
|
||||||
image: gravitl/netmaker-ui:v0.16.0
|
image: gravitl/netmaker-ui:v0.16.1
|
||||||
depends_on:
|
depends_on:
|
||||||
- netmaker
|
- netmaker
|
||||||
links:
|
links:
|
||||||
|
@ -112,26 +113,28 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
- netmaker
|
- netmaker
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
command: ["/mosquitto/config/wait.sh"]
|
||||||
|
environment:
|
||||||
|
NETMAKER_SERVER_HOST: "https://api.NETMAKER_BASE_DOMAIN"
|
||||||
volumes:
|
volumes:
|
||||||
- /root/mosquitto.conf:/mosquitto/config/mosquitto.conf
|
- /root/mosquitto.conf:/mosquitto/config/mosquitto.conf
|
||||||
- /root/mosquitto.passwords:/etc/mosquitto.passwords
|
- /root/wait.sh:/mosquitto/config/wait.sh
|
||||||
- mosquitto_data:/mosquitto/data
|
- mosquitto_data:/mosquitto/data
|
||||||
- mosquitto_logs:/mosquitto/log
|
- mosquitto_logs:/mosquitto/log
|
||||||
- shared_certs:/mosquitto/certs
|
|
||||||
expose:
|
expose:
|
||||||
- "8883"
|
- "8883"
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.tcp.routers.mqtts.rule=HostSNI(`broker.NETMAKER_BASE_DOMAIN`)
|
- traefik.tcp.routers.mqtt.rule=HostSNI(`broker.NETMAKER_BASE_DOMAIN`)
|
||||||
- traefik.tcp.routers.mqtts.tls.passthrough=true
|
- traefik.tcp.routers.mqtt.tls.certresolver=http
|
||||||
- traefik.tcp.services.mqtts-svc.loadbalancer.server.port=8883
|
- traefik.tcp.services.mqtt.loadbalancer.server.port=8883
|
||||||
- traefik.tcp.routers.mqtts.service=mqtts-svc
|
- traefik.tcp.routers.mqtt.entrypoints=websecure
|
||||||
- traefik.tcp.routers.mqtts.entrypoints=websecure
|
|
||||||
prometheus:
|
prometheus:
|
||||||
container_name: prometheus
|
container_name: prometheus
|
||||||
image: gravitl/netmaker-prometheus:latest
|
image: gravitl/netmaker-prometheus:latest
|
||||||
environment:
|
environment:
|
||||||
NETMAKER_METRICS_TARGET: "netmaker-exporter.NETMAKER_BASE_DOMAIN"
|
NETMAKER_METRICS_TARGET: "netmaker-exporter.NETMAKER_BASE_DOMAIN"
|
||||||
|
LICENSE_KEY: "YOUR_LICENSE_KEY"
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.prometheus.entrypoints=websecure
|
- traefik.http.routers.prometheus.entrypoints=websecure
|
||||||
|
@ -157,6 +160,9 @@ services:
|
||||||
environment:
|
environment:
|
||||||
PROMETHEUS_HOST: "prometheus.NETMAKER_BASE_DOMAIN"
|
PROMETHEUS_HOST: "prometheus.NETMAKER_BASE_DOMAIN"
|
||||||
NETMAKER_METRICS_TARGET: "netmaker-exporter.NETMAKER_BASE_DOMAIN"
|
NETMAKER_METRICS_TARGET: "netmaker-exporter.NETMAKER_BASE_DOMAIN"
|
||||||
|
LICENSE_KEY: "YOUR_LICENSE_KEY"
|
||||||
|
volumes:
|
||||||
|
- grafana_data:/var/lib/grafana
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
restart: always
|
restart: always
|
||||||
|
@ -180,18 +186,19 @@ services:
|
||||||
environment:
|
environment:
|
||||||
MQ_HOST: "mq"
|
MQ_HOST: "mq"
|
||||||
MQ_PORT: "443"
|
MQ_PORT: "443"
|
||||||
MQ_SERVER_PORT: "1884"
|
MQ_SERVER_PORT: "1883"
|
||||||
PROMETHEUS: "on"
|
PROMETHEUS: "on"
|
||||||
VERBOSITY: "1"
|
VERBOSITY: "1"
|
||||||
API_PORT: "8085"
|
API_PORT: "8085"
|
||||||
|
LICENSE_KEY: "YOUR_LICENSE_KEY"
|
||||||
PROMETHEUS_HOST: https://prometheus.NETMAKER_BASE_DOMAIN
|
PROMETHEUS_HOST: https://prometheus.NETMAKER_BASE_DOMAIN
|
||||||
expose:
|
expose:
|
||||||
- "8085"
|
- "8085"
|
||||||
volumes:
|
volumes:
|
||||||
traefik_certs: {}
|
traefik_certs: {}
|
||||||
shared_certs: {}
|
|
||||||
sqldata: {}
|
sqldata: {}
|
||||||
dnsconfig: {}
|
dnsconfig: {}
|
||||||
mosquitto_data: {}
|
mosquitto_data: {}
|
||||||
mosquitto_logs: {}
|
mosquitto_logs: {}
|
||||||
prometheus_data: {}
|
prometheus_data: {}
|
||||||
|
grafana_data: {}
|
||||||
|
|
|
@ -3,7 +3,7 @@ version: "3.4"
|
||||||
services:
|
services:
|
||||||
netmaker: # The Primary Server for running Netmaker
|
netmaker: # The Primary Server for running Netmaker
|
||||||
container_name: netmaker
|
container_name: netmaker
|
||||||
image: gravitl/netmaker:v0.16.0
|
image: gravitl/netmaker:v0.16.1
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
- NET_RAW
|
- NET_RAW
|
||||||
|
@ -62,7 +62,7 @@ services:
|
||||||
- traefik.http.services.netmaker-api.loadbalancer.server.port=8081
|
- traefik.http.services.netmaker-api.loadbalancer.server.port=8081
|
||||||
netmaker-ui: # The Netmaker UI Component
|
netmaker-ui: # The Netmaker UI Component
|
||||||
container_name: netmaker-ui
|
container_name: netmaker-ui
|
||||||
image: gravitl/netmaker-ui:v0.16.0
|
image: gravitl/netmaker-ui:v0.16.1
|
||||||
depends_on:
|
depends_on:
|
||||||
- netmaker
|
- netmaker
|
||||||
links:
|
links:
|
||||||
|
|
|
@ -3,7 +3,7 @@ version: "3.4"
|
||||||
services:
|
services:
|
||||||
netmaker:
|
netmaker:
|
||||||
container_name: netmaker
|
container_name: netmaker
|
||||||
image: gravitl/netmaker:v0.16.0
|
image: gravitl/netmaker:v0.16.1
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
- NET_RAW
|
- NET_RAW
|
||||||
|
@ -17,7 +17,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- dnsconfig:/root/config/dnsconfig
|
- dnsconfig:/root/config/dnsconfig
|
||||||
- sqldata:/root/data
|
- sqldata:/root/data
|
||||||
- shared_certs:/etc/netmaker
|
- mosquitto_data:/etc/netmaker
|
||||||
environment:
|
environment:
|
||||||
SERVER_NAME: "broker.NETMAKER_BASE_DOMAIN"
|
SERVER_NAME: "broker.NETMAKER_BASE_DOMAIN"
|
||||||
SERVER_HOST: "SERVER_PUBLIC_IP"
|
SERVER_HOST: "SERVER_PUBLIC_IP"
|
||||||
|
@ -39,6 +39,7 @@ services:
|
||||||
VERBOSITY: "1"
|
VERBOSITY: "1"
|
||||||
MANAGE_IPTABLES: "on"
|
MANAGE_IPTABLES: "on"
|
||||||
PORT_FORWARD_SERVICES: "dns"
|
PORT_FORWARD_SERVICES: "dns"
|
||||||
|
MQ_ADMIN_PASSWORD: "REPLACE_MQ_ADMIN_PASSWORD"
|
||||||
ports:
|
ports:
|
||||||
- "51821-51830:51821-51830/udp"
|
- "51821-51830:51821-51830/udp"
|
||||||
expose:
|
expose:
|
||||||
|
@ -51,7 +52,7 @@ services:
|
||||||
- traefik.http.services.netmaker-api.loadbalancer.server.port=8081
|
- traefik.http.services.netmaker-api.loadbalancer.server.port=8081
|
||||||
netmaker-ui:
|
netmaker-ui:
|
||||||
container_name: netmaker-ui
|
container_name: netmaker-ui
|
||||||
image: gravitl/netmaker-ui:v0.16.0
|
image: gravitl/netmaker-ui:v0.16.1
|
||||||
depends_on:
|
depends_on:
|
||||||
- netmaker
|
- netmaker
|
||||||
links:
|
links:
|
||||||
|
@ -109,24 +110,25 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
- netmaker
|
- netmaker
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
command: ["/mosquitto/config/wait.sh"]
|
||||||
|
environment:
|
||||||
|
NETMAKER_SERVER_HOST: "https://api.NETMAKER_BASE_DOMAIN"
|
||||||
volumes:
|
volumes:
|
||||||
- /root/mosquitto.conf:/mosquitto/config/mosquitto.conf
|
- /root/mosquitto.conf:/mosquitto/config/mosquitto.conf
|
||||||
|
- /root/wait.sh:/mosquitto/config/wait.sh
|
||||||
- mosquitto_data:/mosquitto/data
|
- mosquitto_data:/mosquitto/data
|
||||||
- mosquitto_logs:/mosquitto/log
|
- mosquitto_logs:/mosquitto/log
|
||||||
- shared_certs:/mosquitto/certs
|
|
||||||
expose:
|
expose:
|
||||||
- "8883"
|
- "8883"
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.tcp.routers.mqtts.rule=HostSNI(`broker.NETMAKER_BASE_DOMAIN`)
|
- traefik.tcp.routers.mqtt.rule=HostSNI(`broker.NETMAKER_BASE_DOMAIN`)
|
||||||
- traefik.tcp.routers.mqtts.tls.passthrough=true
|
- traefik.tcp.routers.mqtt.tls.certresolver=http
|
||||||
- traefik.tcp.services.mqtts-svc.loadbalancer.server.port=8883
|
- traefik.tcp.services.mqtt.loadbalancer.server.port=8883
|
||||||
- traefik.tcp.routers.mqtts.service=mqtts-svc
|
- traefik.tcp.routers.mqtt.entrypoints=websecure
|
||||||
- traefik.tcp.routers.mqtts.entrypoints=websecure
|
|
||||||
volumes:
|
volumes:
|
||||||
traefik_certs: {}
|
traefik_certs: {}
|
||||||
shared_certs: {}
|
|
||||||
sqldata: {}
|
sqldata: {}
|
||||||
dnsconfig: {}
|
dnsconfig: {}
|
||||||
mosquitto_data: {}
|
mosquitto_data: {}
|
||||||
mosquitto_logs: {}
|
mosquitto_logs: {}
|
||||||
|
|
|
@ -70,6 +70,7 @@ type ServerConfig struct {
|
||||||
MQServerPort string `yaml:"mqserverport"`
|
MQServerPort string `yaml:"mqserverport"`
|
||||||
Server string `yaml:"server"`
|
Server string `yaml:"server"`
|
||||||
PublicIPService string `yaml:"publicipservice"`
|
PublicIPService string `yaml:"publicipservice"`
|
||||||
|
MQAdminPassword string `yaml:"mqadminpassword"`
|
||||||
MetricsExporter string `yaml:"metrics_exporter"`
|
MetricsExporter string `yaml:"metrics_exporter"`
|
||||||
BasicAuth string `yaml:"basic_auth"`
|
BasicAuth string `yaml:"basic_auth"`
|
||||||
LicenseValue string `yaml:"license_value"`
|
LicenseValue string `yaml:"license_value"`
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
|
@ -59,7 +60,7 @@ func HandleRESTRequests(wg *sync.WaitGroup) {
|
||||||
|
|
||||||
// Relay os.Interrupt to our channel (os.Interrupt = CTRL+C)
|
// Relay os.Interrupt to our channel (os.Interrupt = CTRL+C)
|
||||||
// Ignore other incoming signals
|
// Ignore other incoming signals
|
||||||
ctx, stop := signal.NotifyContext(context.TODO(), os.Interrupt)
|
ctx, stop := signal.NotifyContext(context.TODO(), syscall.SIGTERM, os.Interrupt)
|
||||||
defer stop()
|
defer stop()
|
||||||
|
|
||||||
// Block main routine until a signal is received
|
// Block main routine until a signal is received
|
||||||
|
|
|
@ -1,27 +1,26 @@
|
||||||
//Package classification Netmaker
|
// Package classification Netmaker
|
||||||
//
|
//
|
||||||
// API Usage
|
// # API Usage
|
||||||
//
|
//
|
||||||
// Most actions that can be performed via API can be performed via UI. We recommend managing your networks using the official netmaker-ui project. However, Netmaker can also be run without the UI, and all functions can be achieved via API calls. If your use case requires using Netmaker without the UI or you need to do some troubleshooting/advanced configuration, using the API directly may help.
|
// Most actions that can be performed via API can be performed via UI. We recommend managing your networks using the official netmaker-ui project. However, Netmaker can also be run without the UI, and all functions can be achieved via API calls. If your use case requires using Netmaker without the UI or you need to do some troubleshooting/advanced configuration, using the API directly may help.
|
||||||
//
|
//
|
||||||
//
|
// # Authentication
|
||||||
// Authentication
|
|
||||||
//
|
//
|
||||||
// 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.
|
||||||
//
|
//
|
||||||
// Schemes: https
|
// Schemes: https
|
||||||
// BasePath: /
|
// BasePath: /
|
||||||
// Version: 0.16.0
|
// Version: 0.16.1
|
||||||
// Host: netmaker.io
|
// Host: netmaker.io
|
||||||
//
|
//
|
||||||
// Consumes:
|
// Consumes:
|
||||||
// - application/json
|
// - application/json
|
||||||
//
|
//
|
||||||
// Produces:
|
// Produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
//
|
//
|
||||||
// Security:
|
// Security:
|
||||||
// - oauth
|
// - oauth
|
||||||
//
|
//
|
||||||
// swagger:meta
|
// swagger:meta
|
||||||
package controller
|
package controller
|
||||||
|
@ -310,7 +309,7 @@ type registerRequestBodyParam struct {
|
||||||
RegisterRequest config.RegisterRequest `json:"register_request"`
|
RegisterRequest config.RegisterRequest `json:"register_request"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:response registerResponse
|
// swagger:response registerResponse
|
||||||
type registerResponse struct {
|
type registerResponse struct {
|
||||||
// Register Response
|
// Register Response
|
||||||
// in: body
|
// in: body
|
||||||
|
|
|
@ -6,6 +6,7 @@ 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
|
||||||
|
@ -22,7 +23,7 @@ func checkFreeTierLimits(limit_choice int, next http.Handler) http.HandlerFunc {
|
||||||
Code: http.StatusUnauthorized, Message: "free tier limits exceeded on networks",
|
Code: http.StatusUnauthorized, Message: "free tier limits exceeded on networks",
|
||||||
}
|
}
|
||||||
|
|
||||||
if logic.Free_Tier && logic.Is_EE { // check that free tier limits not exceeded
|
if logic.Free_Tier && servercfg.Is_EE { // 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 {
|
||||||
|
|
|
@ -442,6 +442,20 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, errtype))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, errtype))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Deletes the network role from MQ
|
||||||
|
event := mq.MqDynsecPayload{
|
||||||
|
Commands: []mq.MqDynSecCmd{
|
||||||
|
{
|
||||||
|
Command: mq.DeleteRoleCmd,
|
||||||
|
RoleName: network,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mq.PublishEventToDynSecTopic(event); err != nil {
|
||||||
|
logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
|
||||||
|
event.Commands, err.Error()))
|
||||||
|
}
|
||||||
logger.Log(1, r.Header.Get("user"), "deleted network", network)
|
logger.Log(1, r.Header.Get("user"), "deleted network", network)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode("success")
|
json.NewEncoder(w).Encode("success")
|
||||||
|
@ -488,6 +502,22 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Create Role with acls for the network
|
||||||
|
event := mq.MqDynsecPayload{
|
||||||
|
Commands: []mq.MqDynSecCmd{
|
||||||
|
{
|
||||||
|
Command: mq.CreateRoleCmd,
|
||||||
|
RoleName: network.NetID,
|
||||||
|
Textname: "Network wide role with Acls for nodes",
|
||||||
|
Acls: mq.FetchNetworkAcls(network.NetID),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mq.PublishEventToDynSecTopic(event); err != nil {
|
||||||
|
logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
|
||||||
|
event.Commands, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
if servercfg.IsClientMode() != "off" {
|
if servercfg.IsClientMode() != "off" {
|
||||||
_, err := logic.ServerJoin(&network)
|
_, err := logic.ServerJoin(&network)
|
||||||
|
|
|
@ -67,75 +67,112 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
|
||||||
decoderErr.Error())
|
decoderErr.Error())
|
||||||
logic.ReturnErrorResponse(response, request, errorResponse)
|
logic.ReturnErrorResponse(response, request, errorResponse)
|
||||||
return
|
return
|
||||||
} else {
|
}
|
||||||
errorResponse.Code = http.StatusBadRequest
|
errorResponse.Code = http.StatusBadRequest
|
||||||
if authRequest.ID == "" {
|
if authRequest.ID == "" {
|
||||||
errorResponse.Message = "W1R3: ID can't be empty"
|
errorResponse.Message = "W1R3: ID can't be empty"
|
||||||
logger.Log(0, request.Header.Get("user"), errorResponse.Message)
|
logger.Log(0, request.Header.Get("user"), errorResponse.Message)
|
||||||
|
logic.ReturnErrorResponse(response, request, errorResponse)
|
||||||
|
return
|
||||||
|
} else if authRequest.Password == "" {
|
||||||
|
errorResponse.Message = "W1R3: Password can't be empty"
|
||||||
|
logger.Log(0, request.Header.Get("user"), errorResponse.Message)
|
||||||
|
logic.ReturnErrorResponse(response, request, errorResponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
result, err = logic.GetNodeByID(authRequest.ID)
|
||||||
|
if err != nil {
|
||||||
|
result, err = logic.GetDeletedNodeByID(authRequest.ID)
|
||||||
|
if err != nil {
|
||||||
|
errorResponse.Code = http.StatusBadRequest
|
||||||
|
errorResponse.Message = err.Error()
|
||||||
|
logger.Log(0, request.Header.Get("user"),
|
||||||
|
fmt.Sprintf("failed to get node info [%s]: %v", authRequest.ID, err))
|
||||||
logic.ReturnErrorResponse(response, request, errorResponse)
|
logic.ReturnErrorResponse(response, request, errorResponse)
|
||||||
return
|
return
|
||||||
} else if authRequest.Password == "" {
|
|
||||||
errorResponse.Message = "W1R3: Password can't be empty"
|
|
||||||
logger.Log(0, request.Header.Get("user"), errorResponse.Message)
|
|
||||||
logic.ReturnErrorResponse(response, request, errorResponse)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
result, err = logic.GetNodeByID(authRequest.ID)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
errorResponse.Code = http.StatusBadRequest
|
|
||||||
errorResponse.Message = err.Error()
|
|
||||||
logger.Log(0, request.Header.Get("user"),
|
|
||||||
fmt.Sprintf("failed to get node info [%s]: %v", authRequest.ID, err))
|
|
||||||
logic.ReturnErrorResponse(response, request, errorResponse)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password))
|
|
||||||
if err != nil {
|
|
||||||
errorResponse.Code = http.StatusBadRequest
|
|
||||||
errorResponse.Message = err.Error()
|
|
||||||
logger.Log(0, request.Header.Get("user"),
|
|
||||||
"error validating user password: ", err.Error())
|
|
||||||
logic.ReturnErrorResponse(response, request, errorResponse)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
tokenString, err := logic.CreateJWT(authRequest.ID, authRequest.MacAddress, result.Network)
|
|
||||||
|
|
||||||
if tokenString == "" {
|
|
||||||
errorResponse.Code = http.StatusBadRequest
|
|
||||||
errorResponse.Message = "Could not create Token"
|
|
||||||
logger.Log(0, request.Header.Get("user"),
|
|
||||||
fmt.Sprintf("%s: %v", errorResponse.Message, err))
|
|
||||||
logic.ReturnErrorResponse(response, request, errorResponse)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var successResponse = models.SuccessResponse{
|
|
||||||
Code: http.StatusOK,
|
|
||||||
Message: "W1R3: Device " + authRequest.ID + " Authorized",
|
|
||||||
Response: models.SuccessfulLoginResponse{
|
|
||||||
AuthToken: tokenString,
|
|
||||||
ID: authRequest.ID,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
successJSONResponse, jsonError := json.Marshal(successResponse)
|
|
||||||
|
|
||||||
if jsonError != nil {
|
|
||||||
errorResponse.Code = http.StatusBadRequest
|
|
||||||
errorResponse.Message = err.Error()
|
|
||||||
logger.Log(0, request.Header.Get("user"),
|
|
||||||
"error marshalling resp: ", err.Error())
|
|
||||||
logic.ReturnErrorResponse(response, request, errorResponse)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
response.WriteHeader(http.StatusOK)
|
|
||||||
response.Header().Set("Content-Type", "application/json")
|
|
||||||
response.Write(successJSONResponse)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password))
|
||||||
|
if err != nil {
|
||||||
|
errorResponse.Code = http.StatusBadRequest
|
||||||
|
errorResponse.Message = err.Error()
|
||||||
|
logger.Log(0, request.Header.Get("user"),
|
||||||
|
"error validating user password: ", err.Error())
|
||||||
|
logic.ReturnErrorResponse(response, request, errorResponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// creates network role,node client (added here to resolve any missing configuration in MQ)
|
||||||
|
event := mq.MqDynsecPayload{
|
||||||
|
Commands: []mq.MqDynSecCmd{
|
||||||
|
|
||||||
|
{
|
||||||
|
Command: mq.CreateRoleCmd,
|
||||||
|
RoleName: result.Network,
|
||||||
|
Textname: "Network wide role with Acls for nodes",
|
||||||
|
Acls: mq.FetchNetworkAcls(result.Network),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Command: mq.CreateClientCmd,
|
||||||
|
Username: result.ID,
|
||||||
|
Password: authRequest.Password,
|
||||||
|
Textname: result.Name,
|
||||||
|
Roles: []mq.MqDynSecRole{
|
||||||
|
{
|
||||||
|
Rolename: mq.NodeRole,
|
||||||
|
Priority: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Rolename: result.Network,
|
||||||
|
Priority: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Groups: make([]mq.MqDynSecGroup, 0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mq.PublishEventToDynSecTopic(event); err != nil {
|
||||||
|
logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
|
||||||
|
event.Commands, err.Error()))
|
||||||
|
errorResponse.Code = http.StatusInternalServerError
|
||||||
|
errorResponse.Message = fmt.Sprintf("could not create mq client for node [%s]: %v", result.ID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenString, err := logic.CreateJWT(authRequest.ID, authRequest.MacAddress, result.Network)
|
||||||
|
if tokenString == "" {
|
||||||
|
errorResponse.Code = http.StatusBadRequest
|
||||||
|
errorResponse.Message = "Could not create Token"
|
||||||
|
logger.Log(0, request.Header.Get("user"),
|
||||||
|
fmt.Sprintf("%s: %v", errorResponse.Message, err))
|
||||||
|
logic.ReturnErrorResponse(response, request, errorResponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var successResponse = models.SuccessResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
Message: "W1R3: Device " + authRequest.ID + " Authorized",
|
||||||
|
Response: models.SuccessfulLoginResponse{
|
||||||
|
AuthToken: tokenString,
|
||||||
|
ID: authRequest.ID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
successJSONResponse, jsonError := json.Marshal(successResponse)
|
||||||
|
|
||||||
|
if jsonError != nil {
|
||||||
|
errorResponse.Code = http.StatusBadRequest
|
||||||
|
errorResponse.Message = err.Error()
|
||||||
|
logger.Log(0, request.Header.Get("user"),
|
||||||
|
"error marshalling resp: ", err.Error())
|
||||||
|
logic.ReturnErrorResponse(response, request, errorResponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.WriteHeader(http.StatusOK)
|
||||||
|
response.Header().Set("Content-Type", "application/json")
|
||||||
|
response.Write(successJSONResponse)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// auth middleware for api calls from nodes where node is has not yet joined the server (register, join)
|
// auth middleware for api calls from nodes where node is has not yet joined the server (register, join)
|
||||||
|
@ -226,6 +263,9 @@ func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Ha
|
||||||
if nodesAllowed {
|
if nodesAllowed {
|
||||||
// TODO --- should ensure that node is only operating on itself
|
// TODO --- should ensure that node is only operating on itself
|
||||||
if _, _, _, err := logic.VerifyToken(authToken); err == nil {
|
if _, _, _, err := logic.VerifyToken(authToken); err == nil {
|
||||||
|
|
||||||
|
// this indicates request is from a node
|
||||||
|
// used for failover - if a getNode comes from node, this will trigger a metrics wipe
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -411,6 +451,8 @@ func getNode(w http.ResponseWriter, r *http.Request) {
|
||||||
// set header.
|
// set header.
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
nodeRequest := r.Header.Get("requestfrom") == "node"
|
||||||
|
|
||||||
var params = mux.Vars(r)
|
var params = mux.Vars(r)
|
||||||
nodeid := params["nodeid"]
|
nodeid := params["nodeid"]
|
||||||
node, err := logic.GetNodeByID(nodeid)
|
node, err := logic.GetNodeByID(nodeid)
|
||||||
|
@ -440,6 +482,12 @@ func getNode(w http.ResponseWriter, r *http.Request) {
|
||||||
PeerIDs: peerUpdate.PeerIDs,
|
PeerIDs: peerUpdate.PeerIDs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if servercfg.Is_EE && nodeRequest {
|
||||||
|
if err = logic.EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
|
||||||
|
logger.Log(1, "failed to reset failover list during node config pull", node.Name, node.Network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logger.Log(2, r.Header.Get("user"), "fetched node", params["nodeid"])
|
logger.Log(2, r.Header.Get("user"), "fetched node", params["nodeid"])
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(response)
|
json.NewEncoder(w).Encode(response)
|
||||||
|
@ -585,7 +633,8 @@ func createNode(w http.ResponseWriter, r *http.Request) {
|
||||||
Mine: node.TrafficKeys.Mine,
|
Mine: node.TrafficKeys.Mine,
|
||||||
Server: key,
|
Server: key,
|
||||||
}
|
}
|
||||||
|
// consume password before hashing for mq client creation
|
||||||
|
nodePassword := node.Password
|
||||||
err = logic.CreateNode(&node)
|
err = logic.CreateNode(&node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(0, r.Header.Get("user"),
|
logger.Log(0, r.Header.Get("user"),
|
||||||
|
@ -621,6 +670,44 @@ func createNode(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create client for this node in Mq
|
||||||
|
event := mq.MqDynsecPayload{
|
||||||
|
Commands: []mq.MqDynSecCmd{
|
||||||
|
{ // delete if any client exists already
|
||||||
|
Command: mq.DeleteClientCmd,
|
||||||
|
Username: node.ID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Command: mq.CreateRoleCmd,
|
||||||
|
RoleName: node.Network,
|
||||||
|
Textname: "Network wide role with Acls for nodes",
|
||||||
|
Acls: mq.FetchNetworkAcls(node.Network),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Command: mq.CreateClientCmd,
|
||||||
|
Username: node.ID,
|
||||||
|
Password: nodePassword,
|
||||||
|
Textname: node.Name,
|
||||||
|
Roles: []mq.MqDynSecRole{
|
||||||
|
{
|
||||||
|
Rolename: mq.NodeRole,
|
||||||
|
Priority: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Rolename: node.Network,
|
||||||
|
Priority: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Groups: make([]mq.MqDynSecGroup, 0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mq.PublishEventToDynSecTopic(event); err != nil {
|
||||||
|
logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
|
||||||
|
event.Commands, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
response := models.NodeGet{
|
response := models.NodeGet{
|
||||||
Node: node,
|
Node: node,
|
||||||
Peers: peerUpdate.Peers,
|
Peers: peerUpdate.Peers,
|
||||||
|
@ -756,7 +843,13 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
nodeid := params["nodeid"]
|
nodeid := params["nodeid"]
|
||||||
netid := params["network"]
|
netid := params["network"]
|
||||||
node, err := logic.CreateIngressGateway(netid, nodeid)
|
type failoverData struct {
|
||||||
|
Failover bool `json:"failover"`
|
||||||
|
}
|
||||||
|
var failoverReqBody failoverData
|
||||||
|
json.NewDecoder(r.Body).Decode(&failoverReqBody)
|
||||||
|
|
||||||
|
node, err := logic.CreateIngressGateway(netid, nodeid, failoverReqBody.Failover)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(0, r.Header.Get("user"),
|
logger.Log(0, r.Header.Get("user"),
|
||||||
fmt.Sprintf("failed to create ingress gateway on node [%s] on network [%s]: %v",
|
fmt.Sprintf("failed to create ingress gateway on node [%s] on network [%s]: %v",
|
||||||
|
@ -765,6 +858,12 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if servercfg.Is_EE && failoverReqBody.Failover {
|
||||||
|
if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
|
||||||
|
logger.Log(1, "failed to reset failover list during failover create", node.Name, node.Network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
|
logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(node)
|
json.NewEncoder(w).Encode(node)
|
||||||
|
@ -788,7 +887,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
|
||||||
var params = mux.Vars(r)
|
var params = mux.Vars(r)
|
||||||
nodeid := params["nodeid"]
|
nodeid := params["nodeid"]
|
||||||
netid := params["network"]
|
netid := params["network"]
|
||||||
node, err := logic.DeleteIngressGateway(netid, nodeid)
|
node, wasFailover, err := logic.DeleteIngressGateway(netid, nodeid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(0, r.Header.Get("user"),
|
logger.Log(0, r.Header.Get("user"),
|
||||||
fmt.Sprintf("failed to delete ingress gateway on node [%s] on network [%s]: %v",
|
fmt.Sprintf("failed to delete ingress gateway on node [%s] on network [%s]: %v",
|
||||||
|
@ -797,6 +896,12 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if servercfg.Is_EE && wasFailover {
|
||||||
|
if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
|
||||||
|
logger.Log(1, "failed to reset failover list during failover create", node.Name, node.Network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
|
logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(w).Encode(node)
|
json.NewEncoder(w).Encode(node)
|
||||||
|
@ -880,6 +985,12 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ifaceDelta && servercfg.Is_EE {
|
||||||
|
if err = logic.EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
|
||||||
|
logger.Log(0, "failed to reset failover lists during node update for node", node.Name, node.Network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = logic.UpdateNode(&node, &newNode)
|
err = logic.UpdateNode(&node, &newNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(0, r.Header.Get("user"),
|
logger.Log(0, r.Header.Get("user"),
|
||||||
|
@ -927,12 +1038,23 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
|
||||||
// get params
|
// get params
|
||||||
var params = mux.Vars(r)
|
var params = mux.Vars(r)
|
||||||
var nodeid = params["nodeid"]
|
var nodeid = params["nodeid"]
|
||||||
|
fromNode := r.Header.Get("requestfrom") == "node"
|
||||||
var node, err = logic.GetNodeByID(nodeid)
|
var node, err = logic.GetNodeByID(nodeid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(0, r.Header.Get("user"),
|
if fromNode {
|
||||||
fmt.Sprintf("error fetching node [ %s ] info: %v", nodeid, err))
|
node, err = logic.GetDeletedNodeByID(nodeid)
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
if err != nil {
|
||||||
return
|
logger.Log(0, r.Header.Get("user"),
|
||||||
|
fmt.Sprintf("error fetching node from deleted nodes [ %s ] info: %v", nodeid, err))
|
||||||
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.Log(0, r.Header.Get("user"),
|
||||||
|
fmt.Sprintf("error fetching node [ %s ] info: %v", nodeid, err))
|
||||||
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if isServer(&node) {
|
if isServer(&node) {
|
||||||
err := fmt.Errorf("cannot delete server node")
|
err := fmt.Errorf("cannot delete server node")
|
||||||
|
@ -951,14 +1073,35 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
|
||||||
//send update to node to be deleted before deleting on server otherwise message cannot be sent
|
//send update to node to be deleted before deleting on server otherwise message cannot be sent
|
||||||
node.Action = models.NODE_DELETE
|
node.Action = models.NODE_DELETE
|
||||||
|
|
||||||
err = logic.DeleteNodeByID(&node, false)
|
err = logic.DeleteNodeByID(&node, fromNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if fromNode {
|
||||||
|
// deletes node related role and client
|
||||||
|
event := mq.MqDynsecPayload{
|
||||||
|
Commands: []mq.MqDynSecCmd{
|
||||||
|
{
|
||||||
|
Command: mq.DeleteClientCmd,
|
||||||
|
Username: nodeid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mq.PublishEventToDynSecTopic(event); err != nil {
|
||||||
|
logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
|
||||||
|
event.Commands, err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if servercfg.Is_EE {
|
||||||
|
if err = logic.EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
|
||||||
|
logger.Log(0, "failed to reset failover lists during node delete for node", node.Name, node.Network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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"])
|
||||||
runUpdates(&node, false)
|
runUpdates(&node, false)
|
||||||
runForceServerUpdate(&node, false)
|
runForceServerUpdate(&node, false)
|
||||||
|
|
|
@ -1,28 +1,23 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gravitl/netmaker/logger"
|
|
||||||
"github.com/gravitl/netmaker/logic"
|
"github.com/gravitl/netmaker/logic"
|
||||||
"github.com/gravitl/netmaker/models"
|
"github.com/gravitl/netmaker/models"
|
||||||
"github.com/gravitl/netmaker/netclient/config"
|
|
||||||
"github.com/gravitl/netmaker/servercfg"
|
"github.com/gravitl/netmaker/servercfg"
|
||||||
"github.com/gravitl/netmaker/serverctl"
|
|
||||||
"github.com/gravitl/netmaker/tls"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func serverHandlers(r *mux.Router) {
|
func serverHandlers(r *mux.Router) {
|
||||||
// r.HandleFunc("/api/server/addnetwork/{network}", securityCheckServer(true, http.HandlerFunc(addNetwork))).Methods("POST")
|
// r.HandleFunc("/api/server/addnetwork/{network}", securityCheckServer(true, http.HandlerFunc(addNetwork))).Methods("POST")
|
||||||
|
r.HandleFunc("/api/server/health", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
resp.WriteHeader(http.StatusOK)
|
||||||
|
resp.Write([]byte("Server is up and running!!"))
|
||||||
|
}))
|
||||||
r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))).Methods("GET")
|
r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))).Methods("GET")
|
||||||
r.HandleFunc("/api/server/register", authorize(true, false, "node", http.HandlerFunc(register))).Methods("POST")
|
|
||||||
r.HandleFunc("/api/server/getserverinfo", authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods("GET")
|
r.HandleFunc("/api/server/getserverinfo", authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods("GET")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,13 +49,13 @@ func allowUsers(next http.Handler) http.HandlerFunc {
|
||||||
//
|
//
|
||||||
// Get the server configuration.
|
// Get the server configuration.
|
||||||
//
|
//
|
||||||
// Schemes: https
|
// Schemes: https
|
||||||
//
|
//
|
||||||
// Security:
|
// Security:
|
||||||
// oauth
|
// oauth
|
||||||
//
|
//
|
||||||
// Responses:
|
// Responses:
|
||||||
// 200: serverConfigResponse
|
// 200: serverConfigResponse
|
||||||
func getServerInfo(w http.ResponseWriter, r *http.Request) {
|
func getServerInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
// Set header
|
// Set header
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
@ -75,13 +70,13 @@ func getServerInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
//
|
//
|
||||||
// Get the server configuration.
|
// Get the server configuration.
|
||||||
//
|
//
|
||||||
// Schemes: https
|
// Schemes: https
|
||||||
//
|
//
|
||||||
// Security:
|
// Security:
|
||||||
// oauth
|
// oauth
|
||||||
//
|
//
|
||||||
// Responses:
|
// Responses:
|
||||||
// 200: serverConfigResponse
|
// 200: serverConfigResponse
|
||||||
func getConfig(w http.ResponseWriter, r *http.Request) {
|
func getConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
// Set header
|
// Set header
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
@ -90,83 +85,9 @@ func getConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
scfg := servercfg.GetServerConfig()
|
scfg := servercfg.GetServerConfig()
|
||||||
scfg.IsEE = "no"
|
scfg.IsEE = "no"
|
||||||
if logic.Is_EE {
|
if servercfg.Is_EE {
|
||||||
scfg.IsEE = "yes"
|
scfg.IsEE = "yes"
|
||||||
}
|
}
|
||||||
json.NewEncoder(w).Encode(scfg)
|
json.NewEncoder(w).Encode(scfg)
|
||||||
//w.WriteHeader(http.StatusOK)
|
//w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:route POST /api/server/register server register
|
|
||||||
//
|
|
||||||
// Registers a client with the server and return the Certificate Authority and certificate.
|
|
||||||
//
|
|
||||||
// Schemes: https
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// oauth
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: registerResponse
|
|
||||||
func register(w http.ResponseWriter, r *http.Request) {
|
|
||||||
logger.Log(2, "processing registration request")
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
//decode body
|
|
||||||
var request config.RegisterRequest
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
|
||||||
logger.Log(0, "error decoding request", err.Error())
|
|
||||||
errorResponse := models.ErrorResponse{
|
|
||||||
Code: http.StatusBadRequest, Message: err.Error(),
|
|
||||||
}
|
|
||||||
logic.ReturnErrorResponse(w, r, errorResponse)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cert, ca, err := genCerts(&request.Key, &request.CommonName)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log(0, "failed to generater certs ", err.Error())
|
|
||||||
errorResponse := models.ErrorResponse{
|
|
||||||
Code: http.StatusNotFound, Message: err.Error(),
|
|
||||||
}
|
|
||||||
logic.ReturnErrorResponse(w, r, errorResponse)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//x509.Certificate.PublicKey is an interface therefore json encoding/decoding result in a string value rather than a []byte
|
|
||||||
//include the actual public key so the certificate can be properly reassembled on the other end.
|
|
||||||
response := config.RegisterResponse{
|
|
||||||
CA: *ca,
|
|
||||||
CAPubKey: (ca.PublicKey).(ed25519.PublicKey),
|
|
||||||
Cert: *cert,
|
|
||||||
CertPubKey: (cert.PublicKey).(ed25519.PublicKey),
|
|
||||||
Broker: servercfg.GetServer(),
|
|
||||||
Port: servercfg.GetMQPort(),
|
|
||||||
}
|
|
||||||
logger.Log(2, r.Header.Get("user"),
|
|
||||||
fmt.Sprintf("registered client [%+v] with server", request))
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
json.NewEncoder(w).Encode(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
// genCerts generates a client certificate and returns the certificate and root CA
|
|
||||||
func genCerts(clientKey *ed25519.PrivateKey, name *pkix.Name) (*x509.Certificate, *x509.Certificate, error) {
|
|
||||||
ca, err := serverctl.ReadCertFromDB(tls.ROOT_PEM_NAME)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log(2, "root ca not found ", err.Error())
|
|
||||||
return nil, nil, fmt.Errorf("root ca not found %w", err)
|
|
||||||
}
|
|
||||||
key, err := serverctl.ReadKeyFromDB(tls.ROOT_KEY_NAME)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log(2, "root key not found ", err.Error())
|
|
||||||
return nil, nil, fmt.Errorf("root key not found %w", err)
|
|
||||||
}
|
|
||||||
csr, err := tls.NewCSR(*clientKey, *name)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log(2, "failed to generate client certificate requests", err.Error())
|
|
||||||
return nil, nil, fmt.Errorf("client certification request generation failed %w", err)
|
|
||||||
}
|
|
||||||
cert, err := tls.NewEndEntityCert(*key, csr, ca, tls.CERTIFICATE_VALIDITY)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log(2, "unable to generate client certificate", err.Error())
|
|
||||||
return nil, nil, fmt.Errorf("client certification generation failed %w", err)
|
|
||||||
}
|
|
||||||
return cert, ca, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
per_listener_settings true
|
|
||||||
|
|
||||||
listener 8883
|
|
||||||
allow_anonymous false
|
|
||||||
require_certificate true
|
|
||||||
use_identity_as_username true
|
|
||||||
cafile /mosquitto/certs/root.pem
|
|
||||||
certfile /mosquitto/certs/server.pem
|
|
||||||
keyfile /mosquitto/certs/server.key
|
|
||||||
|
|
||||||
listener 1883
|
|
||||||
allow_anonymous true
|
|
||||||
|
|
||||||
listener 1884
|
|
||||||
allow_anonymous false
|
|
||||||
password_file /etc/mosquitto.passwords
|
|
|
@ -1,12 +1,9 @@
|
||||||
per_listener_settings true
|
per_listener_settings false
|
||||||
|
|
||||||
listener 8883
|
listener 8883
|
||||||
allow_anonymous false
|
allow_anonymous false
|
||||||
require_certificate true
|
|
||||||
use_identity_as_username true
|
|
||||||
cafile /mosquitto/certs/root.pem
|
|
||||||
certfile /mosquitto/certs/server.pem
|
|
||||||
keyfile /mosquitto/certs/server.key
|
|
||||||
|
|
||||||
listener 1883
|
listener 1883
|
||||||
allow_anonymous true
|
allow_anonymous false
|
||||||
|
|
||||||
|
plugin /usr/lib/mosquitto_dynamic_security.so
|
||||||
|
plugin_opt_config_file /mosquitto/data/dynamic-security.json
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
netmaker-exporter:$7$101$9kcXwXP+nUMh06gm$MND2YjtRSvcZTXjMn7xYKoqUFQxG6NOgqWmXIcxxxZksM9cA8732URQWOsPHqpGEvVF9mSVagM1MBEMIKwZm2A==
|
|
23
docker/wait.sh
Executable file
23
docker/wait.sh
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/ash
|
||||||
|
|
||||||
|
wait_for_netmaker() {
|
||||||
|
echo "SERVER: ${NETMAKER_SERVER_HOST}"
|
||||||
|
until curl --output /dev/null --silent --fail --head \
|
||||||
|
--location "${NETMAKER_SERVER_HOST}/api/server/health"; do
|
||||||
|
echo "Waiting for netmaker server to startup"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
main(){
|
||||||
|
# wait for netmaker to startup
|
||||||
|
apk add curl
|
||||||
|
wait_for_netmaker
|
||||||
|
echo "Starting MQ..."
|
||||||
|
# Run the main container command.
|
||||||
|
/docker-entrypoint.sh
|
||||||
|
/usr/sbin/mosquitto -c /mosquitto/config/mosquitto.conf
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main "${@}"
|
|
@ -6,9 +6,11 @@ package ee
|
||||||
import (
|
import (
|
||||||
controller "github.com/gravitl/netmaker/controllers"
|
controller "github.com/gravitl/netmaker/controllers"
|
||||||
"github.com/gravitl/netmaker/ee/ee_controllers"
|
"github.com/gravitl/netmaker/ee/ee_controllers"
|
||||||
|
eelogic "github.com/gravitl/netmaker/ee/logic"
|
||||||
"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/models"
|
||||||
|
"github.com/gravitl/netmaker/servercfg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitEE - Initialize EE Logic
|
// InitEE - Initialize EE Logic
|
||||||
|
@ -21,12 +23,18 @@ func InitEE() {
|
||||||
ValidateLicense()
|
ValidateLicense()
|
||||||
if Limits.FreeTier {
|
if Limits.FreeTier {
|
||||||
logger.Log(0, "proceeding with Free Tier license")
|
logger.Log(0, "proceeding with Free Tier license")
|
||||||
|
logic.SetFreeTierForTelemetry(true)
|
||||||
} else {
|
} else {
|
||||||
logger.Log(0, "proceeding with Paid Tier license")
|
logger.Log(0, "proceeding with Paid Tier license")
|
||||||
|
logic.SetFreeTierForTelemetry(false)
|
||||||
}
|
}
|
||||||
// == End License Handling ==
|
// == End License Handling ==
|
||||||
AddLicenseHooks()
|
AddLicenseHooks()
|
||||||
|
resetFailover()
|
||||||
})
|
})
|
||||||
|
logic.EnterpriseFailoverFunc = eelogic.SetFailover
|
||||||
|
logic.EnterpriseResetFailoverFunc = eelogic.ResetFailover
|
||||||
|
logic.EnterpriseResetAllPeersFailovers = eelogic.WipeAffectedFailoversOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
func setControllerLimits() {
|
func setControllerLimits() {
|
||||||
|
@ -34,7 +42,19 @@ func setControllerLimits() {
|
||||||
logic.Users_Limit = Limits.Users
|
logic.Users_Limit = Limits.Users
|
||||||
logic.Clients_Limit = Limits.Clients
|
logic.Clients_Limit = Limits.Clients
|
||||||
logic.Free_Tier = Limits.FreeTier
|
logic.Free_Tier = Limits.FreeTier
|
||||||
logic.Is_EE = true
|
servercfg.Is_EE = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetFailover() {
|
||||||
|
nets, err := logic.GetNetworks()
|
||||||
|
if err == nil {
|
||||||
|
for _, net := range nets {
|
||||||
|
err = eelogic.ResetFailover(net.NetID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log(0, "failed to reset failover on network", net.NetID, ":", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func retrieveEELogo() string {
|
func retrieveEELogo() string {
|
||||||
|
|
121
ee/logic/failover.go
Normal file
121
ee/logic/failover.go
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
package logic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gravitl/netmaker/logger"
|
||||||
|
"github.com/gravitl/netmaker/logic"
|
||||||
|
"github.com/gravitl/netmaker/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetFailover - finds a suitable failover candidate and sets it
|
||||||
|
func SetFailover(node *models.Node) error {
|
||||||
|
failoverNode := determineFailoverCandidate(node)
|
||||||
|
if failoverNode != nil {
|
||||||
|
return setFailoverNode(failoverNode, node)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetFailover - sets the failover node and wipes disconnected status
|
||||||
|
func ResetFailover(network string) error {
|
||||||
|
nodes, err := logic.GetNetworkNodes(network)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, node := range nodes {
|
||||||
|
err = SetFailover(&node)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log(2, "error setting failover for node", node.Name, ":", err.Error())
|
||||||
|
}
|
||||||
|
err = WipeFailover(node.ID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log(2, "error wiping failover for node", node.Name, ":", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// determineFailoverCandidate - returns a list of nodes that
|
||||||
|
// are suitable for relaying a given node
|
||||||
|
func determineFailoverCandidate(nodeToBeRelayed *models.Node) *models.Node {
|
||||||
|
|
||||||
|
currentNetworkNodes, err := logic.GetNetworkNodes(nodeToBeRelayed.Network)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMetrics, err := logic.GetMetrics(nodeToBeRelayed.ID)
|
||||||
|
if err != nil || currentMetrics == nil || currentMetrics.Connectivity == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
minLatency := int64(9223372036854775807) // max signed int64 value
|
||||||
|
var fastestCandidate *models.Node
|
||||||
|
for i := range currentNetworkNodes {
|
||||||
|
if currentNetworkNodes[i].ID == nodeToBeRelayed.ID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentMetrics.Connectivity[currentNetworkNodes[i].ID].Connected && (currentNetworkNodes[i].Failover == "yes") {
|
||||||
|
if currentMetrics.Connectivity[currentNetworkNodes[i].ID].Latency < int64(minLatency) {
|
||||||
|
fastestCandidate = ¤tNetworkNodes[i]
|
||||||
|
minLatency = currentMetrics.Connectivity[currentNetworkNodes[i].ID].Latency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fastestCandidate
|
||||||
|
}
|
||||||
|
|
||||||
|
// setFailoverNode - changes node's failover node
|
||||||
|
func setFailoverNode(failoverNode, node *models.Node) error {
|
||||||
|
|
||||||
|
node.FailoverNode = failoverNode.ID
|
||||||
|
nodeToUpdate, err := logic.GetNodeByID(node.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if nodeToUpdate.FailoverNode == failoverNode.ID {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return logic.UpdateNode(&nodeToUpdate, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WipeFailover - removes the failover peers of given node (ID)
|
||||||
|
func WipeFailover(nodeid string) error {
|
||||||
|
metrics, err := logic.GetMetrics(nodeid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if metrics != nil {
|
||||||
|
metrics.FailoverPeers = make(map[string]string)
|
||||||
|
return logic.UpdateMetrics(nodeid, metrics)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WipeAffectedFailoversOnly - wipes failovers for nodes that have given node (ID)
|
||||||
|
// in their respective failover lists
|
||||||
|
func WipeAffectedFailoversOnly(nodeid, network string) error {
|
||||||
|
currentNetworkNodes, err := logic.GetNetworkNodes(network)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
WipeFailover(nodeid)
|
||||||
|
|
||||||
|
for i := range currentNetworkNodes {
|
||||||
|
currNodeID := currentNetworkNodes[i].ID
|
||||||
|
if currNodeID == nodeid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
currMetrics, err := logic.GetMetrics(currNodeID)
|
||||||
|
if err != nil || currMetrics == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if currMetrics.FailoverPeers != nil {
|
||||||
|
if len(currMetrics.FailoverPeers[nodeid]) > 0 {
|
||||||
|
WipeFailover(currNodeID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -8,14 +8,10 @@ import (
|
||||||
|
|
||||||
var isEnterprise bool
|
var isEnterprise bool
|
||||||
|
|
||||||
// IsEnterprise - checks if enterprise binary or not
|
|
||||||
func IsEnterprise() bool {
|
|
||||||
return isEnterprise
|
|
||||||
}
|
|
||||||
|
|
||||||
// setIsEnterprise - sets server to use enterprise features
|
// setIsEnterprise - sets server to use enterprise features
|
||||||
func setIsEnterprise() {
|
func setIsEnterprise() {
|
||||||
isEnterprise = true
|
isEnterprise = true
|
||||||
|
logic.SetEEForTelemetry(isEnterprise)
|
||||||
}
|
}
|
||||||
|
|
||||||
// base64encode - base64 encode helper function
|
// base64encode - base64 encode helper function
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -4,7 +4,7 @@ go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/eclipse/paho.mqtt.golang v1.4.1
|
github.com/eclipse/paho.mqtt.golang v1.4.1
|
||||||
github.com/go-playground/validator/v10 v10.11.0
|
github.com/go-playground/validator/v10 v10.11.1
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.2
|
github.com/golang-jwt/jwt/v4 v4.4.2
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
|
@ -41,7 +41,7 @@ require (
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/coreos/go-oidc/v3 v3.4.0
|
github.com/coreos/go-oidc/v3 v3.4.0
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.5.0
|
||||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
|
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
|
||||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035
|
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035
|
||||||
)
|
)
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -171,8 +171,8 @@ github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
|
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||||
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
@ -274,8 +274,9 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH
|
||||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY=
|
github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY=
|
||||||
github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY=
|
github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
|
@ -667,7 +668,6 @@ golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
|
|
@ -16,7 +16,7 @@ spec:
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
containers:
|
containers:
|
||||||
- name: netclient
|
- name: netclient
|
||||||
image: gravitl/netclient-go:v0.16.0
|
image: gravitl/netclient:v0.16.1
|
||||||
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.16.0
|
image: gravitl/netclient:v0.16.1
|
||||||
env:
|
env:
|
||||||
- name: TOKEN
|
- name: TOKEN
|
||||||
value: "TOKEN_VALUE"
|
value: "TOKEN_VALUE"
|
||||||
|
|
|
@ -83,7 +83,7 @@ spec:
|
||||||
value: "Kubernetes"
|
value: "Kubernetes"
|
||||||
- name: VERBOSITY
|
- name: VERBOSITY
|
||||||
value: "3"
|
value: "3"
|
||||||
image: gravitl/netmaker:v0.16.0
|
image: gravitl/netmaker:v0.16.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: netmaker
|
name: netmaker
|
||||||
ports:
|
ports:
|
||||||
|
|
|
@ -15,7 +15,7 @@ spec:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: netmaker-ui
|
- name: netmaker-ui
|
||||||
image: gravitl/netmaker-ui:v0.16.0
|
image: gravitl/netmaker-ui:v0.16.1
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 443
|
- containerPort: 443
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
validator "github.com/go-playground/validator/v10"
|
||||||
"github.com/gravitl/netmaker/database"
|
"github.com/gravitl/netmaker/database"
|
||||||
"github.com/gravitl/netmaker/logger"
|
"github.com/gravitl/netmaker/logger"
|
||||||
"github.com/gravitl/netmaker/models"
|
"github.com/gravitl/netmaker/models"
|
||||||
|
@ -221,7 +221,14 @@ func genKeyName() string {
|
||||||
return strings.Join([]string{"key", entropy.Text(16)[:16]}, "-")
|
return strings.Join([]string{"key", entropy.Text(16)[:16]}, "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenKey - generates random key of length 16
|
||||||
func GenKey() string {
|
func GenKey() string {
|
||||||
entropy, _ := rand.Int(rand.Reader, maxentropy)
|
entropy, _ := rand.Int(rand.Reader, maxentropy)
|
||||||
return entropy.Text(16)[:16]
|
return entropy.Text(16)[:16]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenPassWord - generates random password of length 64
|
||||||
|
func GenPassWord() string {
|
||||||
|
entropy, _ := rand.Int(rand.Reader, maxentropy)
|
||||||
|
return entropy.Text(62)[:64]
|
||||||
|
}
|
||||||
|
|
|
@ -6,12 +6,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
validator "github.com/go-playground/validator/v10"
|
||||||
"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/pro"
|
"github.com/gravitl/netmaker/logic/pro"
|
||||||
"github.com/gravitl/netmaker/models"
|
"github.com/gravitl/netmaker/models"
|
||||||
"github.com/gravitl/netmaker/models/promodels"
|
"github.com/gravitl/netmaker/models/promodels"
|
||||||
|
"github.com/gravitl/netmaker/servercfg"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -140,7 +141,7 @@ func CreateUser(user models.User) (models.User, error) {
|
||||||
|
|
||||||
// legacy
|
// legacy
|
||||||
if StringSliceContains(user.Networks, currentNets[i].NetID) {
|
if StringSliceContains(user.Networks, currentNets[i].NetID) {
|
||||||
if !Is_EE {
|
if !servercfg.Is_EE {
|
||||||
newUser.AccessLevel = pro.NET_ADMIN
|
newUser.AccessLevel = pro.NET_ADMIN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
validator "github.com/go-playground/validator/v10"
|
||||||
"github.com/gravitl/netmaker/database"
|
"github.com/gravitl/netmaker/database"
|
||||||
"github.com/gravitl/netmaker/logger"
|
"github.com/gravitl/netmaker/logger"
|
||||||
"github.com/gravitl/netmaker/models"
|
"github.com/gravitl/netmaker/models"
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/gravitl/netmaker/database"
|
"github.com/gravitl/netmaker/database"
|
||||||
"github.com/gravitl/netmaker/logger"
|
"github.com/gravitl/netmaker/logger"
|
||||||
"github.com/gravitl/netmaker/models"
|
"github.com/gravitl/netmaker/models"
|
||||||
|
"github.com/gravitl/netmaker/servercfg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateEgressGateway - creates an egress gateway
|
// CreateEgressGateway - creates an egress gateway
|
||||||
|
@ -46,6 +47,10 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
|
||||||
postUpCmd := ""
|
postUpCmd := ""
|
||||||
postDownCmd := ""
|
postDownCmd := ""
|
||||||
ipv4, ipv6 := getNetworkProtocols(gateway.Ranges)
|
ipv4, ipv6 := getNetworkProtocols(gateway.Ranges)
|
||||||
|
//no support for ipv6 and ip6tables in netmaker container
|
||||||
|
if node.IsServer == "yes" {
|
||||||
|
ipv6 = false
|
||||||
|
}
|
||||||
logger.Log(3, "creating egress gateway firewall in use is '", node.FirewallInUse, "'")
|
logger.Log(3, "creating egress gateway firewall in use is '", node.FirewallInUse, "'")
|
||||||
if node.OS == "linux" {
|
if node.OS == "linux" {
|
||||||
switch node.FirewallInUse {
|
switch node.FirewallInUse {
|
||||||
|
@ -87,12 +92,12 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
|
||||||
}
|
}
|
||||||
if node.PostUp != "" {
|
if node.PostUp != "" {
|
||||||
if !strings.Contains(node.PostUp, postUpCmd) {
|
if !strings.Contains(node.PostUp, postUpCmd) {
|
||||||
postUpCmd = node.PostUp + " ; " + postUpCmd
|
postUpCmd = node.PostUp + postUpCmd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if node.PostDown != "" {
|
if node.PostDown != "" {
|
||||||
if !strings.Contains(node.PostDown, postDownCmd) {
|
if !strings.Contains(node.PostDown, postDownCmd) {
|
||||||
postDownCmd = node.PostDown + " ; " + postDownCmd
|
postDownCmd = node.PostDown + postDownCmd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +177,7 @@ func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateIngressGateway - creates an ingress gateway
|
// CreateIngressGateway - creates an ingress gateway
|
||||||
func CreateIngressGateway(netid string, nodeid string) (models.Node, error) {
|
func CreateIngressGateway(netid string, nodeid string, failover bool) (models.Node, error) {
|
||||||
|
|
||||||
var postUpCmd, postDownCmd string
|
var postUpCmd, postDownCmd string
|
||||||
node, err := GetNodeByID(nodeid)
|
node, err := GetNodeByID(nodeid)
|
||||||
|
@ -198,6 +203,10 @@ func CreateIngressGateway(netid string, nodeid string) (models.Node, error) {
|
||||||
node.IngressGatewayRange = network.AddressRange
|
node.IngressGatewayRange = network.AddressRange
|
||||||
node.IngressGatewayRange6 = network.AddressRange6
|
node.IngressGatewayRange6 = network.AddressRange6
|
||||||
ipv4, ipv6 := getNetworkProtocols(cidrs)
|
ipv4, ipv6 := getNetworkProtocols(cidrs)
|
||||||
|
//no support for ipv6 and ip6tables in netmaker container
|
||||||
|
if node.IsServer == "yes" {
|
||||||
|
ipv6 = false
|
||||||
|
}
|
||||||
logger.Log(3, "creating ingress gateway firewall in use is '", node.FirewallInUse, "'")
|
logger.Log(3, "creating ingress gateway firewall in use is '", node.FirewallInUse, "'")
|
||||||
switch node.FirewallInUse {
|
switch node.FirewallInUse {
|
||||||
case models.FIREWALL_NFTABLES:
|
case models.FIREWALL_NFTABLES:
|
||||||
|
@ -224,7 +233,9 @@ func CreateIngressGateway(netid string, nodeid string) (models.Node, error) {
|
||||||
node.PostUp = postUpCmd
|
node.PostUp = postUpCmd
|
||||||
node.PostDown = postDownCmd
|
node.PostDown = postDownCmd
|
||||||
node.UDPHolePunch = "no"
|
node.UDPHolePunch = "no"
|
||||||
|
if failover && servercfg.Is_EE {
|
||||||
|
node.Failover = "yes"
|
||||||
|
}
|
||||||
data, err := json.Marshal(&node)
|
data, err := json.Marshal(&node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Node{}, err
|
return models.Node{}, err
|
||||||
|
@ -238,26 +249,27 @@ func CreateIngressGateway(netid string, nodeid string) (models.Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteIngressGateway - deletes an ingress gateway
|
// DeleteIngressGateway - deletes an ingress gateway
|
||||||
func DeleteIngressGateway(networkName string, nodeid string) (models.Node, error) {
|
func DeleteIngressGateway(networkName string, nodeid string) (models.Node, bool, error) {
|
||||||
|
|
||||||
node, err := GetNodeByID(nodeid)
|
node, err := GetNodeByID(nodeid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Node{}, err
|
return models.Node{}, false, err
|
||||||
}
|
}
|
||||||
network, err := GetParentNetwork(networkName)
|
network, err := GetParentNetwork(networkName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Node{}, err
|
return models.Node{}, false, err
|
||||||
}
|
}
|
||||||
// delete ext clients belonging to ingress gateway
|
// delete ext clients belonging to ingress gateway
|
||||||
if err = DeleteGatewayExtClients(node.ID, networkName); err != nil {
|
if err = DeleteGatewayExtClients(node.ID, networkName); err != nil {
|
||||||
return models.Node{}, err
|
return models.Node{}, false, err
|
||||||
}
|
}
|
||||||
logger.Log(3, "deleting ingress gateway")
|
logger.Log(3, "deleting ingress gateway")
|
||||||
|
wasFailover := node.Failover == "yes"
|
||||||
node.UDPHolePunch = network.DefaultUDPHolePunch
|
node.UDPHolePunch = network.DefaultUDPHolePunch
|
||||||
node.LastModified = time.Now().Unix()
|
node.LastModified = time.Now().Unix()
|
||||||
node.IsIngressGateway = "no"
|
node.IsIngressGateway = "no"
|
||||||
node.IngressGatewayRange = ""
|
node.IngressGatewayRange = ""
|
||||||
|
node.Failover = "no"
|
||||||
|
|
||||||
// default to removing postup and postdown
|
// default to removing postup and postdown
|
||||||
node.PostUp = ""
|
node.PostUp = ""
|
||||||
|
@ -274,14 +286,14 @@ func DeleteIngressGateway(networkName string, nodeid string) (models.Node, error
|
||||||
|
|
||||||
data, err := json.Marshal(&node)
|
data, err := json.Marshal(&node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Node{}, err
|
return models.Node{}, false, err
|
||||||
}
|
}
|
||||||
err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
|
err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Node{}, err
|
return models.Node{}, wasFailover, err
|
||||||
}
|
}
|
||||||
err = SetNetworkNodesLastModified(networkName)
|
err = SetNetworkNodesLastModified(networkName)
|
||||||
return node, err
|
return node, wasFailover, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteGatewayExtClients - deletes ext clients based on gateway (mac) of ingress node and network
|
// DeleteGatewayExtClients - deletes ext clients based on gateway (mac) of ingress node and network
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-ping/ping"
|
"github.com/go-ping/ping"
|
||||||
"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/models"
|
||||||
|
"github.com/gravitl/netmaker/netclient/wireguard"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,6 +22,14 @@ func Collect(iface string, peerMap models.PeerMap) (*models.Metrics, error) {
|
||||||
return &metrics, err
|
return &metrics, err
|
||||||
}
|
}
|
||||||
defer wgclient.Close()
|
defer wgclient.Close()
|
||||||
|
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
iface, err = wireguard.GetRealIface(iface)
|
||||||
|
if err != nil {
|
||||||
|
fillUnconnectedData(&metrics, peerMap)
|
||||||
|
return &metrics, err
|
||||||
|
}
|
||||||
|
}
|
||||||
device, err := wgclient.Device(iface)
|
device, err := wgclient.Device(iface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fillUnconnectedData(&metrics, peerMap)
|
fillUnconnectedData(&metrics, peerMap)
|
||||||
|
@ -58,11 +68,24 @@ func Collect(iface string, peerMap models.PeerMap) (*models.Metrics, error) {
|
||||||
newMetric.Latency = 999
|
newMetric.Latency = 999
|
||||||
} else {
|
} else {
|
||||||
pingStats := pinger.Statistics()
|
pingStats := pinger.Statistics()
|
||||||
newMetric.Uptime = 1
|
if pingStats.PacketsRecv > 0 {
|
||||||
newMetric.Connected = true
|
newMetric.Uptime = 1
|
||||||
newMetric.Latency = pingStats.AvgRtt.Milliseconds()
|
newMetric.Connected = true
|
||||||
|
newMetric.Latency = pingStats.AvgRtt.Milliseconds()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check device peer to see if WG is working if ping failed
|
||||||
|
if !newMetric.Connected {
|
||||||
|
if currPeer.ReceiveBytes > 0 &&
|
||||||
|
currPeer.TransmitBytes > 0 &&
|
||||||
|
time.Now().Before(currPeer.LastHandshakeTime.Add(time.Minute<<1)) {
|
||||||
|
newMetric.Connected = true
|
||||||
|
newMetric.Uptime = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
newMetric.TotalTime = 1
|
newMetric.TotalTime = 1
|
||||||
metrics.Connectivity[id] = newMetric
|
metrics.Connectivity[id] = newMetric
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/c-robinson/iplib"
|
"github.com/c-robinson/iplib"
|
||||||
"github.com/go-playground/validator/v10"
|
validator "github.com/go-playground/validator/v10"
|
||||||
"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/acls/nodeacls"
|
"github.com/gravitl/netmaker/logic/acls/nodeacls"
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
validator "github.com/go-playground/validator/v10"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gravitl/netmaker/database"
|
"github.com/gravitl/netmaker/database"
|
||||||
"github.com/gravitl/netmaker/logger"
|
"github.com/gravitl/netmaker/logger"
|
||||||
|
@ -186,7 +186,9 @@ func DeleteNodeByID(node *models.Node, exterminate bool) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err = database.DeleteRecord(database.NODES_TABLE_NAME, key); err != nil {
|
if err = database.DeleteRecord(database.NODES_TABLE_NAME, key); err != nil {
|
||||||
return err
|
if !database.IsEmptyRecord(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if servercfg.IsDNSMode() {
|
if servercfg.IsDNSMode() {
|
||||||
|
@ -212,6 +214,7 @@ func DeleteNodeByID(node *models.Node, exterminate bool) error {
|
||||||
if node.IsServer == "yes" {
|
if node.IsServer == "yes" {
|
||||||
return removeLocalServer(node)
|
return removeLocalServer(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,6 +253,20 @@ func ValidateNode(node *models.Node, isUpdate bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsFailoverPresent - checks if a node is marked as a failover in given network
|
||||||
|
func IsFailoverPresent(network string) bool {
|
||||||
|
netNodes, err := GetNetworkNodes(network)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range netNodes {
|
||||||
|
if netNodes[i].Failover == "yes" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// CreateNode - creates a node in database
|
// CreateNode - creates a node in database
|
||||||
func CreateNode(node *models.Node) error {
|
func CreateNode(node *models.Node) error {
|
||||||
|
|
||||||
|
@ -480,6 +497,7 @@ func SetNodeDefaults(node *models.Node) {
|
||||||
node.SetDefaultIsHub()
|
node.SetDefaultIsHub()
|
||||||
node.SetDefaultConnected()
|
node.SetDefaultConnected()
|
||||||
node.SetDefaultACL()
|
node.SetDefaultACL()
|
||||||
|
node.SetDefaultFailover()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRecordKey - get record key
|
// GetRecordKey - get record key
|
||||||
|
|
175
logic/peers.go
175
logic/peers.go
|
@ -33,6 +33,16 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
|
||||||
}
|
}
|
||||||
var peerMap = make(models.PeerMap)
|
var peerMap = make(models.PeerMap)
|
||||||
|
|
||||||
|
var metrics *models.Metrics
|
||||||
|
if servercfg.Is_EE {
|
||||||
|
metrics, _ = GetMetrics(node.ID)
|
||||||
|
}
|
||||||
|
if metrics == nil {
|
||||||
|
metrics = &models.Metrics{}
|
||||||
|
}
|
||||||
|
if metrics.FailoverPeers == nil {
|
||||||
|
metrics.FailoverPeers = make(map[string]string)
|
||||||
|
}
|
||||||
// udppeers = the peers parsed from the local interface
|
// udppeers = the peers parsed from the local interface
|
||||||
// gives us correct port to reach
|
// gives us correct port to reach
|
||||||
udppeers, errN := database.GetPeers(node.Network)
|
udppeers, errN := database.GetPeers(node.Network)
|
||||||
|
@ -61,6 +71,10 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
|
||||||
if node.NetworkSettings.IsPointToSite == "yes" && node.IsHub == "no" && peer.IsHub == "no" {
|
if node.NetworkSettings.IsPointToSite == "yes" && node.IsHub == "no" && peer.IsHub == "no" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if node.Connected != "yes" {
|
||||||
|
//skip unconnected nodes
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// if the node is not a server, set the endpoint
|
// if the node is not a server, set the endpoint
|
||||||
var setEndpoint = !(node.IsServer == "yes")
|
var setEndpoint = !(node.IsServer == "yes")
|
||||||
|
@ -81,7 +95,10 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
|
||||||
if isP2S && peer.IsHub != "yes" {
|
if isP2S && peer.IsHub != "yes" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if len(metrics.FailoverPeers[peer.ID]) > 0 && IsFailoverPresent(node.Network) {
|
||||||
|
logger.Log(2, "peer", peer.Name, peer.PrimaryAddress(), "was found to be in failover peers list for node", node.Name, node.PrimaryAddress())
|
||||||
|
continue
|
||||||
|
}
|
||||||
pubkey, err := wgtypes.ParseKey(peer.PublicKey)
|
pubkey, err := wgtypes.ParseKey(peer.PublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.PeerUpdate{}, err
|
return models.PeerUpdate{}, err
|
||||||
|
@ -134,8 +151,8 @@ func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
|
||||||
return models.PeerUpdate{}, err
|
return models.PeerUpdate{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set_allowedips
|
|
||||||
allowedips := GetAllowedIPs(node, &peer)
|
allowedips := GetAllowedIPs(node, &peer, metrics)
|
||||||
var keepalive time.Duration
|
var keepalive time.Duration
|
||||||
if node.PersistentKeepalive != 0 {
|
if node.PersistentKeepalive != 0 {
|
||||||
// set_keepalive
|
// set_keepalive
|
||||||
|
@ -243,64 +260,9 @@ func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, e
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllowedIPs - calculates the wireguard allowedip field for a peer of a node based on the peer and node settings
|
// GetAllowedIPs - calculates the wireguard allowedip field for a peer of a node based on the peer and node settings
|
||||||
func GetAllowedIPs(node, peer *models.Node) []net.IPNet {
|
func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet {
|
||||||
var allowedips []net.IPNet
|
var allowedips []net.IPNet
|
||||||
|
allowedips = getNodeAllowedIPs(peer, node)
|
||||||
if peer.Address != "" {
|
|
||||||
var peeraddr = net.IPNet{
|
|
||||||
IP: net.ParseIP(peer.Address),
|
|
||||||
Mask: net.CIDRMask(32, 32),
|
|
||||||
}
|
|
||||||
allowedips = append(allowedips, peeraddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if peer.Address6 != "" {
|
|
||||||
var addr6 = net.IPNet{
|
|
||||||
IP: net.ParseIP(peer.Address6),
|
|
||||||
Mask: net.CIDRMask(128, 128),
|
|
||||||
}
|
|
||||||
allowedips = append(allowedips, addr6)
|
|
||||||
}
|
|
||||||
// handle manually set peers
|
|
||||||
for _, allowedIp := range peer.AllowedIPs {
|
|
||||||
|
|
||||||
// parsing as a CIDR first. If valid CIDR, append
|
|
||||||
if _, ipnet, err := net.ParseCIDR(allowedIp); err == nil {
|
|
||||||
nodeEndpointArr := strings.Split(node.Endpoint, ":")
|
|
||||||
if !ipnet.Contains(net.IP(nodeEndpointArr[0])) && ipnet.IP.String() != peer.Address { // don't need to add an allowed ip that already exists..
|
|
||||||
allowedips = append(allowedips, *ipnet)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else { // parsing as an IP second. If valid IP, check if ipv4 or ipv6, then append
|
|
||||||
if iplib.Version(net.ParseIP(allowedIp)) == 4 && allowedIp != peer.Address {
|
|
||||||
ipnet := net.IPNet{
|
|
||||||
IP: net.ParseIP(allowedIp),
|
|
||||||
Mask: net.CIDRMask(32, 32),
|
|
||||||
}
|
|
||||||
allowedips = append(allowedips, ipnet)
|
|
||||||
} else if iplib.Version(net.ParseIP(allowedIp)) == 6 && allowedIp != peer.Address6 {
|
|
||||||
ipnet := net.IPNet{
|
|
||||||
IP: net.ParseIP(allowedIp),
|
|
||||||
Mask: net.CIDRMask(128, 128),
|
|
||||||
}
|
|
||||||
allowedips = append(allowedips, ipnet)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// handle egress gateway peers
|
|
||||||
if peer.IsEgressGateway == "yes" {
|
|
||||||
//hasGateway = true
|
|
||||||
egressIPs := getEgressIPs(node, peer)
|
|
||||||
// remove internet gateway if server
|
|
||||||
if node.IsServer == "yes" {
|
|
||||||
for i := len(egressIPs) - 1; i >= 0; i-- {
|
|
||||||
if egressIPs[i].String() == "0.0.0.0/0" || egressIPs[i].String() == "::/0" {
|
|
||||||
egressIPs = append(egressIPs[:i], egressIPs[i+1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allowedips = append(allowedips, egressIPs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle ingress gateway peers
|
// handle ingress gateway peers
|
||||||
if peer.IsIngressGateway == "yes" {
|
if peer.IsIngressGateway == "yes" {
|
||||||
|
@ -311,6 +273,27 @@ func GetAllowedIPs(node, peer *models.Node) []net.IPNet {
|
||||||
for _, extPeer := range extPeers {
|
for _, extPeer := range extPeers {
|
||||||
allowedips = append(allowedips, extPeer.AllowedIPs...)
|
allowedips = append(allowedips, extPeer.AllowedIPs...)
|
||||||
}
|
}
|
||||||
|
// if node is a failover node, add allowed ips from nodes it is handling
|
||||||
|
if peer.Failover == "yes" && metrics.FailoverPeers != nil {
|
||||||
|
// traverse through nodes that need handling
|
||||||
|
logger.Log(3, "peer", peer.Name, "was found to be failover for", node.Name, "checking failover peers...")
|
||||||
|
for k := range metrics.FailoverPeers {
|
||||||
|
// if FailoverNode is me for this node, add allowedips
|
||||||
|
if metrics.FailoverPeers[k] == peer.ID {
|
||||||
|
// get original node so we can traverse the allowed ips
|
||||||
|
nodeToFailover, err := GetNodeByID(k)
|
||||||
|
if err == nil {
|
||||||
|
failoverNodeMetrics, err := GetMetrics(nodeToFailover.ID)
|
||||||
|
if err == nil && failoverNodeMetrics != nil {
|
||||||
|
if len(failoverNodeMetrics.NodeName) > 0 {
|
||||||
|
allowedips = append(allowedips, getNodeAllowedIPs(&nodeToFailover, peer)...)
|
||||||
|
logger.Log(0, "failing over node", nodeToFailover.Name, nodeToFailover.PrimaryAddress(), "to failover node", peer.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// handle relay gateway peers
|
// handle relay gateway peers
|
||||||
if peer.IsRelay == "yes" {
|
if peer.IsRelay == "yes" {
|
||||||
|
@ -462,6 +445,17 @@ func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//add egress range if relay is egress
|
||||||
|
if relay.IsEgressGateway == "yes" {
|
||||||
|
var ip *net.IPNet
|
||||||
|
for _, cidr := range relay.EgressGatewayRanges {
|
||||||
|
_, ip, err = net.ParseCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allowedips = append(allowedips, *ip)
|
||||||
|
}
|
||||||
|
|
||||||
pubkey, err := wgtypes.ParseKey(relay.PublicKey)
|
pubkey, err := wgtypes.ParseKey(relay.PublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -555,3 +549,64 @@ func getEgressIPs(node, peer *models.Node) []net.IPNet {
|
||||||
}
|
}
|
||||||
return allowedips
|
return allowedips
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
|
||||||
|
var allowedips = []net.IPNet{}
|
||||||
|
|
||||||
|
if peer.Address != "" {
|
||||||
|
var peeraddr = net.IPNet{
|
||||||
|
IP: net.ParseIP(peer.Address),
|
||||||
|
Mask: net.CIDRMask(32, 32),
|
||||||
|
}
|
||||||
|
allowedips = append(allowedips, peeraddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if peer.Address6 != "" {
|
||||||
|
var addr6 = net.IPNet{
|
||||||
|
IP: net.ParseIP(peer.Address6),
|
||||||
|
Mask: net.CIDRMask(128, 128),
|
||||||
|
}
|
||||||
|
allowedips = append(allowedips, addr6)
|
||||||
|
}
|
||||||
|
// handle manually set peers
|
||||||
|
for _, allowedIp := range peer.AllowedIPs {
|
||||||
|
|
||||||
|
// parsing as a CIDR first. If valid CIDR, append
|
||||||
|
if _, ipnet, err := net.ParseCIDR(allowedIp); err == nil {
|
||||||
|
nodeEndpointArr := strings.Split(node.Endpoint, ":")
|
||||||
|
if !ipnet.Contains(net.IP(nodeEndpointArr[0])) && ipnet.IP.String() != peer.Address { // don't need to add an allowed ip that already exists..
|
||||||
|
allowedips = append(allowedips, *ipnet)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { // parsing as an IP second. If valid IP, check if ipv4 or ipv6, then append
|
||||||
|
if iplib.Version(net.ParseIP(allowedIp)) == 4 && allowedIp != peer.Address {
|
||||||
|
ipnet := net.IPNet{
|
||||||
|
IP: net.ParseIP(allowedIp),
|
||||||
|
Mask: net.CIDRMask(32, 32),
|
||||||
|
}
|
||||||
|
allowedips = append(allowedips, ipnet)
|
||||||
|
} else if iplib.Version(net.ParseIP(allowedIp)) == 6 && allowedIp != peer.Address6 {
|
||||||
|
ipnet := net.IPNet{
|
||||||
|
IP: net.ParseIP(allowedIp),
|
||||||
|
Mask: net.CIDRMask(128, 128),
|
||||||
|
}
|
||||||
|
allowedips = append(allowedips, ipnet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// handle egress gateway peers
|
||||||
|
if peer.IsEgressGateway == "yes" {
|
||||||
|
//hasGateway = true
|
||||||
|
egressIPs := getEgressIPs(node, peer)
|
||||||
|
// remove internet gateway if server
|
||||||
|
if node.IsServer == "yes" {
|
||||||
|
for i := len(egressIPs) - 1; i >= 0; i-- {
|
||||||
|
if egressIPs[i].String() == "0.0.0.0/0" || egressIPs[i].String() == "::/0" {
|
||||||
|
egressIPs = append(egressIPs[:i], egressIPs[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allowedips = append(allowedips, egressIPs...)
|
||||||
|
}
|
||||||
|
return allowedips
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,17 @@ import (
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
var EnterpriseCheckFuncs []interface{}
|
// EnterpriseCheckFuncs - can be set to run functions for EE
|
||||||
|
var EnterpriseCheckFuncs []func()
|
||||||
|
|
||||||
|
// EnterpriseFailoverFunc - interface to control failover funcs
|
||||||
|
var EnterpriseFailoverFunc func(node *models.Node) error
|
||||||
|
|
||||||
|
// EnterpriseResetFailoverFunc - interface to control reset failover funcs
|
||||||
|
var EnterpriseResetFailoverFunc func(network string) error
|
||||||
|
|
||||||
|
// EnterpriseResetAllPeersFailovers - resets all nodes that are considering a node to be failover worthy (inclusive)
|
||||||
|
var EnterpriseResetAllPeersFailovers func(nodeid, network string) error
|
||||||
|
|
||||||
// == Join, Checkin, and Leave for Server ==
|
// == Join, Checkin, and Leave for Server ==
|
||||||
|
|
||||||
|
@ -169,7 +179,7 @@ func ServerJoin(networkSettings *models.Network) (models.Node, error) {
|
||||||
// EnterpriseCheck - Runs enterprise functions if presented
|
// EnterpriseCheck - Runs enterprise functions if presented
|
||||||
func EnterpriseCheck() {
|
func EnterpriseCheck() {
|
||||||
for _, check := range EnterpriseCheckFuncs {
|
for _, check := range EnterpriseCheckFuncs {
|
||||||
check.(func())()
|
check()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,6 @@ var (
|
||||||
Clients_Limit = 1000000000
|
Clients_Limit = 1000000000
|
||||||
// Free_Tier - specifies if free tier
|
// Free_Tier - specifies if free tier
|
||||||
Free_Tier = false
|
Free_Tier = false
|
||||||
// Is_EE - specifies if enterprise
|
|
||||||
Is_EE = false
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// constant for database key for storing server ids
|
// constant for database key for storing server ids
|
||||||
|
|
|
@ -10,12 +10,28 @@ import (
|
||||||
"github.com/posthog/posthog-go"
|
"github.com/posthog/posthog-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// flags to keep for telemetry
|
||||||
|
var isFreeTier bool
|
||||||
|
var isEE bool
|
||||||
|
|
||||||
// posthog_pub_key - Key for sending data to PostHog
|
// posthog_pub_key - Key for sending data to PostHog
|
||||||
const posthog_pub_key = "phc_1vEXhPOA1P7HP5jP2dVU9xDTUqXHAelmtravyZ1vvES"
|
const posthog_pub_key = "phc_1vEXhPOA1P7HP5jP2dVU9xDTUqXHAelmtravyZ1vvES"
|
||||||
|
|
||||||
// posthog_endpoint - Endpoint of PostHog server
|
// posthog_endpoint - Endpoint of PostHog server
|
||||||
const posthog_endpoint = "https://app.posthog.com"
|
const posthog_endpoint = "https://app.posthog.com"
|
||||||
|
|
||||||
|
// setEEForTelemetry - store EE flag without having an import cycle when used for telemetry
|
||||||
|
// (as the ee package needs the logic package as currently written).
|
||||||
|
func SetEEForTelemetry(eeFlag bool) {
|
||||||
|
isEE = eeFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
// setFreeTierForTelemetry - store free tier flag without having an import cycle when used for telemetry
|
||||||
|
// (as the ee package needs the logic package as currently written).
|
||||||
|
func SetFreeTierForTelemetry(freeTierFlag bool) {
|
||||||
|
isFreeTier = freeTierFlag
|
||||||
|
}
|
||||||
|
|
||||||
// sendTelemetry - gathers telemetry data and sends to posthog
|
// sendTelemetry - gathers telemetry data and sends to posthog
|
||||||
func sendTelemetry() error {
|
func sendTelemetry() error {
|
||||||
if servercfg.Telemetry() == "off" {
|
if servercfg.Telemetry() == "off" {
|
||||||
|
@ -54,7 +70,9 @@ func sendTelemetry() error {
|
||||||
Set("freebsd", d.Count.FreeBSD).
|
Set("freebsd", d.Count.FreeBSD).
|
||||||
Set("docker", d.Count.Docker).
|
Set("docker", d.Count.Docker).
|
||||||
Set("k8s", d.Count.K8S).
|
Set("k8s", d.Count.K8S).
|
||||||
Set("version", d.Version),
|
Set("version", d.Version).
|
||||||
|
Set("is_ee", isEE).
|
||||||
|
Set("is_free_tier", isFreeTier),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +162,8 @@ type telemetryData struct {
|
||||||
Networks int
|
Networks int
|
||||||
Servers int
|
Servers int
|
||||||
Version string
|
Version string
|
||||||
|
IsEE bool
|
||||||
|
IsFreeTier bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// clientCount - What types of netclients we're tallying
|
// clientCount - What types of netclients we're tallying
|
||||||
|
|
|
@ -218,3 +218,11 @@ func StringDifference(a, b []string) []string {
|
||||||
}
|
}
|
||||||
return diff
|
return diff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckIfFileExists - checks if file exists or not in the given path
|
||||||
|
func CheckIfFileExists(filePath string) bool {
|
||||||
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
156
main.go
156
main.go
|
@ -3,9 +3,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/rand"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
@ -14,7 +11,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gravitl/netmaker/auth"
|
"github.com/gravitl/netmaker/auth"
|
||||||
"github.com/gravitl/netmaker/config"
|
"github.com/gravitl/netmaker/config"
|
||||||
|
@ -29,7 +25,6 @@ import (
|
||||||
"github.com/gravitl/netmaker/netclient/ncutils"
|
"github.com/gravitl/netmaker/netclient/ncutils"
|
||||||
"github.com/gravitl/netmaker/servercfg"
|
"github.com/gravitl/netmaker/servercfg"
|
||||||
"github.com/gravitl/netmaker/serverctl"
|
"github.com/gravitl/netmaker/serverctl"
|
||||||
"github.com/gravitl/netmaker/tls"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "dev"
|
var version = "dev"
|
||||||
|
@ -134,10 +129,6 @@ func initialize() { // Client Mode Prereq Check
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = genCerts(); err != nil {
|
|
||||||
logger.Log(0, "something went wrong when generating broker certs", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if servercfg.IsMessageQueueBackend() {
|
if servercfg.IsMessageQueueBackend() {
|
||||||
if err = mq.ServerStartNotify(); err != nil {
|
if err = mq.ServerStartNotify(); err != nil {
|
||||||
logger.Log(0, "error occurred when notifying nodes of startup", err.Error())
|
logger.Log(0, "error occurred when notifying nodes of startup", err.Error())
|
||||||
|
@ -154,6 +145,12 @@ func startControllers() {
|
||||||
logger.Log(0, "error occurred initializing DNS: ", err.Error())
|
logger.Log(0, "error occurred initializing DNS: ", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if servercfg.IsMessageQueueBackend() {
|
||||||
|
if err := mq.Configure(); err != nil {
|
||||||
|
logger.FatalLog("failed to configure MQ: ", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Run Rest Server
|
//Run Rest Server
|
||||||
if servercfg.IsRestBackend() {
|
if servercfg.IsRestBackend() {
|
||||||
if !servercfg.DisableRemoteIPCheck() && servercfg.GetAPIHost() == "127.0.0.1" {
|
if !servercfg.DisableRemoteIPCheck() && servercfg.GetAPIHost() == "127.0.0.1" {
|
||||||
|
@ -165,7 +162,6 @@ func startControllers() {
|
||||||
waitnetwork.Add(1)
|
waitnetwork.Add(1)
|
||||||
go controller.HandleRESTRequests(&waitnetwork)
|
go controller.HandleRESTRequests(&waitnetwork)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Run MessageQueue
|
//Run MessageQueue
|
||||||
if servercfg.IsMessageQueueBackend() {
|
if servercfg.IsMessageQueueBackend() {
|
||||||
waitnetwork.Add(1)
|
waitnetwork.Add(1)
|
||||||
|
@ -184,6 +180,7 @@ func runMessageQueue(wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
brokerHost, secure := servercfg.GetMessageQueueEndpoint()
|
brokerHost, secure := servercfg.GetMessageQueueEndpoint()
|
||||||
logger.Log(0, "connecting to mq broker at", brokerHost, "with TLS?", fmt.Sprintf("%v", secure))
|
logger.Log(0, "connecting to mq broker at", brokerHost, "with TLS?", fmt.Sprintf("%v", secure))
|
||||||
|
mq.SetUpAdminClient()
|
||||||
mq.SetupMQTT()
|
mq.SetupMQTT()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
go mq.Keepalive(ctx)
|
go mq.Keepalive(ctx)
|
||||||
|
@ -206,142 +203,3 @@ func setGarbageCollection() {
|
||||||
debug.SetGCPercent(ncutils.DEFAULT_GC_PERCENT)
|
debug.SetGCPercent(ncutils.DEFAULT_GC_PERCENT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func genCerts() error {
|
|
||||||
logger.Log(0, "checking keys and certificates")
|
|
||||||
var private *ed25519.PrivateKey
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// == ROOT key handling ==
|
|
||||||
|
|
||||||
private, err = serverctl.ReadKeyFromDB(tls.ROOT_KEY_NAME)
|
|
||||||
if errors.Is(err, os.ErrNotExist) || database.IsEmptyRecord(err) {
|
|
||||||
logger.Log(0, "generating new root key")
|
|
||||||
_, newKey, err := ed25519.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
private = &newKey
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Log(2, "saving root.key")
|
|
||||||
if err := serverctl.SaveKey(functions.GetNetmakerPath()+ncutils.GetSeparator(), tls.ROOT_KEY_NAME, *private); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// == ROOT cert handling ==
|
|
||||||
|
|
||||||
ca, err := serverctl.ReadCertFromDB(tls.ROOT_PEM_NAME)
|
|
||||||
//if cert doesn't exist or will expire within 10 days --- but can't do this as clients won't be able to connect
|
|
||||||
//if errors.Is(err, os.ErrNotExist) || cert.NotAfter.Before(time.Now().Add(time.Hour*24*10)) {
|
|
||||||
if errors.Is(err, os.ErrNotExist) || database.IsEmptyRecord(err) || ca.NotAfter.Before(time.Now().Add(time.Hour*24*10)) {
|
|
||||||
logger.Log(0, "generating new root CA")
|
|
||||||
caName := tls.NewName("CA Root", "US", "Gravitl")
|
|
||||||
csr, err := tls.NewCSR(*private, caName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rootCA, err := tls.SelfSignedCA(*private, csr, tls.CERTIFICATE_VALIDITY)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ca = rootCA
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Log(2, "saving root.pem")
|
|
||||||
if err := serverctl.SaveCert(functions.GetNetmakerPath()+ncutils.GetSeparator(), tls.ROOT_PEM_NAME, ca); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// == SERVER cert handling ==
|
|
||||||
|
|
||||||
cert, err := serverctl.ReadCertFromDB(tls.SERVER_PEM_NAME)
|
|
||||||
if errors.Is(err, os.ErrNotExist) || database.IsEmptyRecord(err) || cert.NotAfter.Before(time.Now().Add(time.Hour*24*10)) {
|
|
||||||
//gen new key
|
|
||||||
logger.Log(0, "generating new server key/certificate")
|
|
||||||
_, key, err := ed25519.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
serverName := tls.NewCName(servercfg.GetServer())
|
|
||||||
csr, err := tls.NewCSR(key, serverName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
newCert, err := tls.NewEndEntityCert(*private, csr, ca, tls.CERTIFICATE_VALIDITY)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := serverctl.SaveKey(functions.GetNetmakerPath()+ncutils.GetSeparator(), tls.SERVER_KEY_NAME, key); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cert = newCert
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
} else if err == nil {
|
|
||||||
if serverKey, err := serverctl.ReadKeyFromDB(tls.SERVER_KEY_NAME); err == nil {
|
|
||||||
logger.Log(2, "saving server.key")
|
|
||||||
if err := serverctl.SaveKey(functions.GetNetmakerPath()+ncutils.GetSeparator(), tls.SERVER_KEY_NAME, *serverKey); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.Log(2, "saving server.pem")
|
|
||||||
if err := serverctl.SaveCert(functions.GetNetmakerPath()+ncutils.GetSeparator(), tls.SERVER_PEM_NAME, cert); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// == SERVER-CLIENT connection cert handling ==
|
|
||||||
|
|
||||||
serverClientCert, err := serverctl.ReadCertFromDB(tls.SERVER_CLIENT_PEM)
|
|
||||||
if errors.Is(err, os.ErrNotExist) || database.IsEmptyRecord(err) || serverClientCert.NotAfter.Before(time.Now().Add(time.Hour*24*10)) {
|
|
||||||
//gen new key
|
|
||||||
logger.Log(0, "generating new server client key/certificate")
|
|
||||||
_, key, err := ed25519.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
serverName := tls.NewCName(tls.SERVER_CLIENT_ENTRY)
|
|
||||||
csr, err := tls.NewCSR(key, serverName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
newServerClientCert, err := tls.NewEndEntityCert(*private, csr, ca, tls.CERTIFICATE_VALIDITY)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := serverctl.SaveKey(functions.GetNetmakerPath()+ncutils.GetSeparator(), tls.SERVER_CLIENT_KEY, key); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
serverClientCert = newServerClientCert
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
} else if err == nil {
|
|
||||||
logger.Log(2, "saving serverclient.key")
|
|
||||||
if serverClientKey, err := serverctl.ReadKeyFromDB(tls.SERVER_CLIENT_KEY); err == nil {
|
|
||||||
if err := serverctl.SaveKey(functions.GetNetmakerPath()+ncutils.GetSeparator(), tls.SERVER_CLIENT_KEY, *serverClientKey); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Log(2, "saving serverclient.pem")
|
|
||||||
if err := serverctl.SaveCert(functions.GetNetmakerPath()+ncutils.GetSeparator(), tls.SERVER_CLIENT_PEM, serverClientCert); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Log(1, "ensure the root.pem, root.key, server.pem, and server.key files are updated on your broker")
|
|
||||||
|
|
||||||
return serverctl.SetClientTLSConf(
|
|
||||||
functions.GetNetmakerPath()+ncutils.GetSeparator()+tls.SERVER_CLIENT_PEM,
|
|
||||||
functions.GetNetmakerPath()+ncutils.GetSeparator()+tls.SERVER_CLIENT_KEY,
|
|
||||||
ca,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,11 +4,12 @@ import "time"
|
||||||
|
|
||||||
// Metrics - metrics struct
|
// Metrics - metrics struct
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
Network string `json:"network" bson:"network" yaml:"network"`
|
Network string `json:"network" bson:"network" yaml:"network"`
|
||||||
NodeID string `json:"node_id" bson:"node_id" yaml:"node_id"`
|
NodeID string `json:"node_id" bson:"node_id" yaml:"node_id"`
|
||||||
NodeName string `json:"node_name" bson:"node_name" yaml:"node_name"`
|
NodeName string `json:"node_name" bson:"node_name" yaml:"node_name"`
|
||||||
IsServer string `json:"isserver" bson:"isserver" yaml:"isserver" validate:"checkyesorno"`
|
IsServer string `json:"isserver" bson:"isserver" yaml:"isserver" validate:"checkyesorno"`
|
||||||
Connectivity map[string]Metric `json:"connectivity" bson:"connectivity" yaml:"connectivity"`
|
Connectivity map[string]Metric `json:"connectivity" bson:"connectivity" yaml:"connectivity"`
|
||||||
|
FailoverPeers map[string]string `json:"needsfailover" bson:"needsfailover" yaml:"needsfailover"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metric - holds a metric for data between nodes
|
// Metric - holds a metric for data between nodes
|
||||||
|
|
|
@ -82,6 +82,7 @@ type Node struct {
|
||||||
EgressGatewayNatEnabled string `json:"egressgatewaynatenabled" bson:"egressgatewaynatenabled" yaml:"egressgatewaynatenabled"`
|
EgressGatewayNatEnabled string `json:"egressgatewaynatenabled" bson:"egressgatewaynatenabled" yaml:"egressgatewaynatenabled"`
|
||||||
EgressGatewayRequest EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"`
|
EgressGatewayRequest EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"`
|
||||||
RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"`
|
RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"`
|
||||||
|
FailoverNode string `json:"failovernode" bson:"failovernode" yaml:"failovernode"`
|
||||||
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"`
|
||||||
// IsStatic - refers to if the Endpoint is set manually or dynamically
|
// IsStatic - refers to if the Endpoint is set manually or dynamically
|
||||||
|
@ -104,6 +105,7 @@ type Node struct {
|
||||||
// == 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"`
|
||||||
|
Failover string `json:"failover" bson:"failover" yaml:"failover" validate:"checkyesorno"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodesArray - used for node sorting
|
// NodesArray - used for node sorting
|
||||||
|
@ -297,6 +299,13 @@ func (node *Node) SetDefaultName() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Node.SetDefaultFailover - sets default value of failover status to no if not set
|
||||||
|
func (node *Node) SetDefaultFailover() {
|
||||||
|
if node.Failover == "" {
|
||||||
|
node.Failover = "no"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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) { // TODO add new field for nftables present
|
||||||
newNode.ID = currentNode.ID
|
newNode.ID = currentNode.ID
|
||||||
|
@ -452,6 +461,10 @@ func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftable
|
||||||
newNode.DefaultACL = currentNode.DefaultACL
|
newNode.DefaultACL = currentNode.DefaultACL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newNode.Failover == "" {
|
||||||
|
newNode.Failover = currentNode.Failover
|
||||||
|
}
|
||||||
|
|
||||||
newNode.TrafficKeys = currentNode.TrafficKeys
|
newNode.TrafficKeys = currentNode.TrafficKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,7 @@ type ServerConfig struct {
|
||||||
Version string `yaml:"version"`
|
Version string `yaml:"version"`
|
||||||
MQPort string `yaml:"mqport"`
|
MQPort string `yaml:"mqport"`
|
||||||
Server string `yaml:"server"`
|
Server string `yaml:"server"`
|
||||||
|
Is_EE bool `yaml:"isee"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// User.NameInCharset - returns if name is in charset below or not
|
// User.NameInCharset - returns if name is in charset below or not
|
||||||
|
|
196
mq/dynsec.go
Normal file
196
mq/dynsec.go
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
package mq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
|
"github.com/gravitl/netmaker/functions"
|
||||||
|
"github.com/gravitl/netmaker/logger"
|
||||||
|
"github.com/gravitl/netmaker/logic"
|
||||||
|
"github.com/gravitl/netmaker/netclient/ncutils"
|
||||||
|
"github.com/gravitl/netmaker/servercfg"
|
||||||
|
"golang.org/x/crypto/pbkdf2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// mq client for admin
|
||||||
|
var mqAdminClient mqtt.Client
|
||||||
|
|
||||||
|
const (
|
||||||
|
// constant for client command
|
||||||
|
CreateClientCmd = "createClient"
|
||||||
|
// constant for disable command
|
||||||
|
DisableClientCmd = "disableClient"
|
||||||
|
// constant for delete client command
|
||||||
|
DeleteClientCmd = "deleteClient"
|
||||||
|
// constant for modify client command
|
||||||
|
ModifyClientCmd = "modifyClient"
|
||||||
|
|
||||||
|
// constant for create role command
|
||||||
|
CreateRoleCmd = "createRole"
|
||||||
|
// constant for delete role command
|
||||||
|
DeleteRoleCmd = "deleteRole"
|
||||||
|
|
||||||
|
// constant for admin user name
|
||||||
|
mqAdminUserName = "Netmaker-Admin"
|
||||||
|
// constant for server user name
|
||||||
|
mqNetmakerServerUserName = "Netmaker-Server"
|
||||||
|
// constant for exporter user name
|
||||||
|
mqExporterUserName = "Netmaker-Exporter"
|
||||||
|
|
||||||
|
// DynamicSecSubTopic - constant for dynamic security subscription topic
|
||||||
|
dynamicSecSubTopic = "$CONTROL/dynamic-security/#"
|
||||||
|
// DynamicSecPubTopic - constant for dynamic security subscription topic
|
||||||
|
dynamicSecPubTopic = "$CONTROL/dynamic-security/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// struct for dynamic security file
|
||||||
|
type dynJSON struct {
|
||||||
|
Clients []client `json:"clients"`
|
||||||
|
Roles []role `json:"roles"`
|
||||||
|
DefaultAcl defaultAccessAcl `json:"defaultACLAccess"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct for client role
|
||||||
|
type clientRole struct {
|
||||||
|
Rolename string `json:"rolename"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct for MQ client
|
||||||
|
type client struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
TextName string `json:"textName"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Salt string `json:"salt"`
|
||||||
|
Iterations int `json:"iterations"`
|
||||||
|
Roles []clientRole `json:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct for MQ role
|
||||||
|
type role struct {
|
||||||
|
Rolename string `json:"rolename"`
|
||||||
|
Acls []Acl `json:"acls"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct for default acls
|
||||||
|
type defaultAccessAcl struct {
|
||||||
|
PublishClientSend bool `json:"publishClientSend"`
|
||||||
|
PublishClientReceive bool `json:"publishClientReceive"`
|
||||||
|
Subscribe bool `json:"subscribe"`
|
||||||
|
Unsubscribe bool `json:"unsubscribe"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MqDynSecGroup - struct for MQ client group
|
||||||
|
type MqDynSecGroup struct {
|
||||||
|
Groupname string `json:"groupname"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MqDynSecRole - struct for MQ client role
|
||||||
|
type MqDynSecRole struct {
|
||||||
|
Rolename string `json:"rolename"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acl - struct for MQ acls
|
||||||
|
type Acl struct {
|
||||||
|
AclType string `json:"acltype"`
|
||||||
|
Topic string `json:"topic"`
|
||||||
|
Priority int `json:"priority,omitempty"`
|
||||||
|
Allow bool `json:"allow"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MqDynSecCmd - struct for MQ dynamic security command
|
||||||
|
type MqDynSecCmd struct {
|
||||||
|
Command string `json:"command"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
RoleName string `json:"rolename,omitempty"`
|
||||||
|
Acls []Acl `json:"acls,omitempty"`
|
||||||
|
Clientid string `json:"clientid"`
|
||||||
|
Textname string `json:"textname"`
|
||||||
|
Textdescription string `json:"textdescription"`
|
||||||
|
Groups []MqDynSecGroup `json:"groups"`
|
||||||
|
Roles []MqDynSecRole `json:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MqDynsecPayload - struct for dynamic security command payload
|
||||||
|
type MqDynsecPayload struct {
|
||||||
|
Commands []MqDynSecCmd `json:"commands"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodePasswordToPBKDF2 - encodes the given password with PBKDF2 hashing for MQ
|
||||||
|
func encodePasswordToPBKDF2(password string, salt string, iterations int, keyLength int) string {
|
||||||
|
binaryEncoded := pbkdf2.Key([]byte(password), []byte(salt), iterations, keyLength, sha512.New)
|
||||||
|
return base64.StdEncoding.EncodeToString(binaryEncoded)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure - configures the dynamic initial configuration for MQ
|
||||||
|
func Configure() error {
|
||||||
|
path := functions.GetNetmakerPath() + ncutils.GetSeparator() + dynamicSecurityFile
|
||||||
|
if logic.CheckIfFileExists(path) {
|
||||||
|
logger.Log(0, "MQ Is Already Configured, Skipping...")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if servercfg.Is_EE {
|
||||||
|
dynConfig.Clients = append(dynConfig.Clients, exporterMQClient)
|
||||||
|
dynConfig.Roles = append(dynConfig.Roles, exporterMQRole)
|
||||||
|
}
|
||||||
|
password := servercfg.GetMqAdminPassword()
|
||||||
|
if password == "" {
|
||||||
|
return errors.New("MQ admin password not provided")
|
||||||
|
}
|
||||||
|
for i, cI := range dynConfig.Clients {
|
||||||
|
if cI.Username == mqAdminUserName || cI.Username == mqNetmakerServerUserName {
|
||||||
|
salt := logic.RandomString(12)
|
||||||
|
hashed := encodePasswordToPBKDF2(password, salt, 101, 64)
|
||||||
|
cI.Password = hashed
|
||||||
|
cI.Iterations = 101
|
||||||
|
cI.Salt = base64.StdEncoding.EncodeToString([]byte(salt))
|
||||||
|
dynConfig.Clients[i] = cI
|
||||||
|
} else if servercfg.Is_EE && cI.Username == mqExporterUserName {
|
||||||
|
exporterPassword := servercfg.GetLicenseKey()
|
||||||
|
salt := logic.RandomString(12)
|
||||||
|
hashed := encodePasswordToPBKDF2(exporterPassword, salt, 101, 64)
|
||||||
|
cI.Password = hashed
|
||||||
|
cI.Iterations = 101
|
||||||
|
cI.Salt = base64.StdEncoding.EncodeToString([]byte(salt))
|
||||||
|
dynConfig.Clients[i] = cI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data, err := json.MarshalIndent(dynConfig, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.WriteFile(path, data, 0755)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublishEventToDynSecTopic - publishes the message to dynamic security topic
|
||||||
|
func PublishEventToDynSecTopic(payload MqDynsecPayload) error {
|
||||||
|
|
||||||
|
d, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var connecterr error
|
||||||
|
if token := mqAdminClient.Publish(dynamicSecPubTopic, 2, false, d); !token.WaitTimeout(MQ_TIMEOUT*time.Second) || token.Error() != nil {
|
||||||
|
if token.Error() == nil {
|
||||||
|
connecterr = errors.New("connect timeout")
|
||||||
|
} else {
|
||||||
|
connecterr = token.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return connecterr
|
||||||
|
}
|
||||||
|
|
||||||
|
// watchDynSecTopic - message handler for dynamic security responses
|
||||||
|
func watchDynSecTopic(client mqtt.Client, msg mqtt.Message) {
|
||||||
|
|
||||||
|
logger.Log(1, fmt.Sprintf("----->WatchDynSecTopic Message: %+v", string(msg.Payload())))
|
||||||
|
|
||||||
|
}
|
384
mq/dynsec_helper.go
Normal file
384
mq/dynsec_helper.go
Normal file
|
@ -0,0 +1,384 @@
|
||||||
|
package mq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
|
"github.com/gravitl/netmaker/servercfg"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// constant for admin role
|
||||||
|
adminRole = "admin"
|
||||||
|
// constant for server role
|
||||||
|
serverRole = "server"
|
||||||
|
// constant for exporter role
|
||||||
|
exporterRole = "exporter"
|
||||||
|
// constant for node role
|
||||||
|
NodeRole = "node"
|
||||||
|
|
||||||
|
// const for dynamic security file
|
||||||
|
dynamicSecurityFile = "dynamic-security.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// default configuration of dynamic security
|
||||||
|
dynConfig = dynJSON{
|
||||||
|
Clients: []client{
|
||||||
|
{
|
||||||
|
Username: mqAdminUserName,
|
||||||
|
TextName: "netmaker admin user",
|
||||||
|
Password: "",
|
||||||
|
Salt: "",
|
||||||
|
Iterations: 0,
|
||||||
|
Roles: []clientRole{
|
||||||
|
{
|
||||||
|
Rolename: adminRole,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Username: mqNetmakerServerUserName,
|
||||||
|
TextName: "netmaker server user",
|
||||||
|
Password: "",
|
||||||
|
Salt: "",
|
||||||
|
Iterations: 0,
|
||||||
|
Roles: []clientRole{
|
||||||
|
{
|
||||||
|
Rolename: serverRole,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Roles: []role{
|
||||||
|
{
|
||||||
|
Rolename: adminRole,
|
||||||
|
Acls: fetchAdminAcls(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Rolename: serverRole,
|
||||||
|
Acls: fetchServerAcls(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Rolename: NodeRole,
|
||||||
|
Acls: fetchNodeAcls(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultAcl: defaultAccessAcl{
|
||||||
|
PublishClientSend: false,
|
||||||
|
PublishClientReceive: true,
|
||||||
|
Subscribe: false,
|
||||||
|
Unsubscribe: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
exporterMQClient = client{
|
||||||
|
Username: mqExporterUserName,
|
||||||
|
TextName: "netmaker metrics exporter",
|
||||||
|
Password: "",
|
||||||
|
Salt: "",
|
||||||
|
Iterations: 101,
|
||||||
|
Roles: []clientRole{
|
||||||
|
{
|
||||||
|
Rolename: exporterRole,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
exporterMQRole = role{
|
||||||
|
Rolename: exporterRole,
|
||||||
|
Acls: fetchExporterAcls(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// DynListCLientsCmdResp - struct for list clients response from MQ
|
||||||
|
type DynListCLientsCmdResp struct {
|
||||||
|
Responses []struct {
|
||||||
|
Command string `json:"command"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
Data ListClientsData `json:"data"`
|
||||||
|
} `json:"responses"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListClientsData - struct for list clients data
|
||||||
|
type ListClientsData struct {
|
||||||
|
Clients []string `json:"clients"`
|
||||||
|
TotalCount int `json:"totalCount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAdminClient - fetches admin client of the MQ
|
||||||
|
func GetAdminClient() (mqtt.Client, error) {
|
||||||
|
opts := mqtt.NewClientOptions()
|
||||||
|
setMqOptions(mqAdminUserName, servercfg.GetMqAdminPassword(), opts)
|
||||||
|
mqclient := mqtt.NewClient(opts)
|
||||||
|
var connecterr error
|
||||||
|
if token := mqclient.Connect(); !token.WaitTimeout(MQ_TIMEOUT*time.Second) || token.Error() != nil {
|
||||||
|
if token.Error() == nil {
|
||||||
|
connecterr = errors.New("connect timeout")
|
||||||
|
} else {
|
||||||
|
connecterr = token.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mqclient, connecterr
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListClients - to list all clients in the MQ
|
||||||
|
func ListClients(client mqtt.Client) (ListClientsData, error) {
|
||||||
|
respChan := make(chan mqtt.Message, 10)
|
||||||
|
defer close(respChan)
|
||||||
|
command := "listClients"
|
||||||
|
resp := ListClientsData{}
|
||||||
|
msg := MqDynsecPayload{
|
||||||
|
Commands: []MqDynSecCmd{
|
||||||
|
{
|
||||||
|
Command: command,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
client.Subscribe("$CONTROL/dynamic-security/v1/response", 2, mqtt.MessageHandler(func(c mqtt.Client, m mqtt.Message) {
|
||||||
|
respChan <- m
|
||||||
|
}))
|
||||||
|
defer client.Unsubscribe()
|
||||||
|
d, _ := json.Marshal(msg)
|
||||||
|
token := client.Publish("$CONTROL/dynamic-security/v1", 2, true, d)
|
||||||
|
if !token.WaitTimeout(30) || token.Error() != nil {
|
||||||
|
var err error
|
||||||
|
if token.Error() == nil {
|
||||||
|
err = errors.New("connection timeout")
|
||||||
|
} else {
|
||||||
|
err = token.Error()
|
||||||
|
}
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for m := range respChan {
|
||||||
|
msg := DynListCLientsCmdResp{}
|
||||||
|
json.Unmarshal(m.Payload(), &msg)
|
||||||
|
for _, mI := range msg.Responses {
|
||||||
|
if mI.Command == command {
|
||||||
|
return mI.Data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp, errors.New("resp not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchNetworkAcls - fetches network acls
|
||||||
|
func FetchNetworkAcls(network string) []Acl {
|
||||||
|
return []Acl{
|
||||||
|
{
|
||||||
|
AclType: "publishClientReceive",
|
||||||
|
Topic: fmt.Sprintf("update/%s/#", network),
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientReceive",
|
||||||
|
Topic: fmt.Sprintf("peers/%s/#", network),
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "subscribePattern",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "unsubscribePattern",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// serverAcls - fetches server role related acls
|
||||||
|
func fetchServerAcls() []Acl {
|
||||||
|
return []Acl{
|
||||||
|
{
|
||||||
|
AclType: "publishClientSend",
|
||||||
|
Topic: "peers/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientSend",
|
||||||
|
Topic: "update/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientSend",
|
||||||
|
Topic: "metrics_exporter",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientReceive",
|
||||||
|
Topic: "ping/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientReceive",
|
||||||
|
Topic: "update/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientReceive",
|
||||||
|
Topic: "signal/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientReceive",
|
||||||
|
Topic: "metrics/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "subscribePattern",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "unsubscribePattern",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchNodeAcls - fetches node related acls
|
||||||
|
func fetchNodeAcls() []Acl {
|
||||||
|
// keeping node acls generic as of now.
|
||||||
|
return []Acl{
|
||||||
|
|
||||||
|
{
|
||||||
|
AclType: "publishClientSend",
|
||||||
|
Topic: "signal/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientSend",
|
||||||
|
Topic: "update/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientSend",
|
||||||
|
Topic: "ping/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientSend",
|
||||||
|
Topic: "metrics/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "subscribePattern",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "unsubscribePattern",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchExporterAcls - fetch exporter role related acls
|
||||||
|
func fetchExporterAcls() []Acl {
|
||||||
|
return []Acl{
|
||||||
|
{
|
||||||
|
AclType: "publishClientReceive",
|
||||||
|
Topic: "metrics_exporter",
|
||||||
|
Allow: true,
|
||||||
|
Priority: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "subscribePattern",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "unsubscribePattern",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchAdminAcls - fetches admin role related acls
|
||||||
|
func fetchAdminAcls() []Acl {
|
||||||
|
return []Acl{
|
||||||
|
{
|
||||||
|
AclType: "publishClientSend",
|
||||||
|
Topic: "$CONTROL/dynamic-security/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientReceive",
|
||||||
|
Topic: "$CONTROL/dynamic-security/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "subscribePattern",
|
||||||
|
Topic: "$CONTROL/dynamic-security/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientReceive",
|
||||||
|
Topic: "$SYS/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "subscribePattern",
|
||||||
|
Topic: "$SYS/#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientReceive",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "subscribePattern",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "unsubscribePattern",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AclType: "publishClientSend",
|
||||||
|
Topic: "#",
|
||||||
|
Priority: -1,
|
||||||
|
Allow: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -86,6 +86,12 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
|
||||||
logger.Log(1, "error unmarshaling payload ", err.Error())
|
logger.Log(1, "error unmarshaling payload ", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ifaceDelta := logic.IfaceDelta(¤tNode, &newNode)
|
||||||
|
if servercfg.Is_EE && ifaceDelta {
|
||||||
|
if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID, currentNode.Network); err != nil {
|
||||||
|
logger.Log(1, "failed to reset failover list during node update", currentNode.Name, currentNode.Network)
|
||||||
|
}
|
||||||
|
}
|
||||||
newNode.SetLastCheckIn()
|
newNode.SetLastCheckIn()
|
||||||
if err := logic.UpdateNode(¤tNode, &newNode); err != nil {
|
if err := logic.UpdateNode(¤tNode, &newNode); err != nil {
|
||||||
logger.Log(1, "error saving node", err.Error())
|
logger.Log(1, "error saving node", err.Error())
|
||||||
|
@ -98,7 +104,7 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
|
||||||
|
|
||||||
// UpdateMetrics message Handler -- handles updates from client nodes for metrics
|
// UpdateMetrics message Handler -- handles updates from client nodes for metrics
|
||||||
func UpdateMetrics(client mqtt.Client, msg mqtt.Message) {
|
func UpdateMetrics(client mqtt.Client, msg mqtt.Message) {
|
||||||
if logic.Is_EE {
|
if servercfg.Is_EE {
|
||||||
go func() {
|
go func() {
|
||||||
id, err := getID(msg.Topic())
|
id, err := getID(msg.Topic())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -122,7 +128,7 @@ func UpdateMetrics(client mqtt.Client, msg mqtt.Message) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNodeMetrics(¤tNode, &newMetrics)
|
shouldUpdate := updateNodeMetrics(¤tNode, &newMetrics)
|
||||||
|
|
||||||
if err = logic.UpdateMetrics(id, &newMetrics); err != nil {
|
if err = logic.UpdateMetrics(id, &newMetrics); err != nil {
|
||||||
logger.Log(1, "faield to update node metrics", id, currentNode.Name, err.Error())
|
logger.Log(1, "faield to update node metrics", id, currentNode.Name, err.Error())
|
||||||
|
@ -135,6 +141,20 @@ func UpdateMetrics(client mqtt.Client, msg mqtt.Message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newMetrics.Connectivity != nil {
|
||||||
|
err := logic.EnterpriseFailoverFunc(¤tNode)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log(0, "failed to failover for node", currentNode.Name, "on network", currentNode.Network, "-", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldUpdate {
|
||||||
|
logger.Log(2, "updating peers after node", currentNode.Name, currentNode.Network, "detected connectivity issues")
|
||||||
|
if err = PublishSinglePeerUpdate(¤tNode); err != nil {
|
||||||
|
logger.Log(0, "failed to publish update after failover peer change for node", currentNode.Name, currentNode.Network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logger.Log(1, "updated node metrics", id, currentNode.Name)
|
logger.Log(1, "updated node metrics", id, currentNode.Name)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -194,11 +214,17 @@ func updateNodePeers(currentNode *models.Node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) {
|
func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) bool {
|
||||||
|
if newMetrics.FailoverPeers == nil {
|
||||||
|
newMetrics.FailoverPeers = make(map[string]string)
|
||||||
|
}
|
||||||
oldMetrics, err := logic.GetMetrics(currentNode.ID)
|
oldMetrics, err := logic.GetMetrics(currentNode.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(1, "error finding old metrics for node", currentNode.ID, currentNode.Name)
|
logger.Log(1, "error finding old metrics for node", currentNode.ID, currentNode.Name)
|
||||||
return
|
return false
|
||||||
|
}
|
||||||
|
if oldMetrics.FailoverPeers == nil {
|
||||||
|
oldMetrics.FailoverPeers = make(map[string]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
var attachedClients []models.ExtClient
|
var attachedClients []models.ExtClient
|
||||||
|
@ -225,14 +251,45 @@ func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) {
|
||||||
oldMetric := oldMetrics.Connectivity[k]
|
oldMetric := oldMetrics.Connectivity[k]
|
||||||
currMetric.TotalTime += oldMetric.TotalTime
|
currMetric.TotalTime += oldMetric.TotalTime
|
||||||
currMetric.Uptime += oldMetric.Uptime // get the total uptime for this connection
|
currMetric.Uptime += oldMetric.Uptime // get the total uptime for this connection
|
||||||
currMetric.PercentUp = 100.0 * (float64(currMetric.Uptime) / float64(currMetric.TotalTime))
|
if currMetric.Uptime == 0 || currMetric.TotalTime == 0 {
|
||||||
totalUpMinutes := currMetric.Uptime * 5
|
currMetric.PercentUp = 0
|
||||||
|
} else {
|
||||||
|
currMetric.PercentUp = 100.0 * (float64(currMetric.Uptime) / float64(currMetric.TotalTime))
|
||||||
|
}
|
||||||
|
totalUpMinutes := currMetric.Uptime * ncutils.CheckInInterval
|
||||||
currMetric.ActualUptime = time.Duration(totalUpMinutes) * time.Minute
|
currMetric.ActualUptime = time.Duration(totalUpMinutes) * time.Minute
|
||||||
delete(oldMetrics.Connectivity, k) // remove from old data
|
delete(oldMetrics.Connectivity, k) // remove from old data
|
||||||
newMetrics.Connectivity[k] = currMetric
|
newMetrics.Connectivity[k] = currMetric
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add nodes that need failover
|
||||||
|
nodes, err := logic.GetNetworkNodes(currentNode.Network)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log(0, "failed to retrieve nodes while updating metrics")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, node := range nodes {
|
||||||
|
if !newMetrics.Connectivity[node.ID].Connected &&
|
||||||
|
len(newMetrics.Connectivity[node.ID].NodeName) > 0 &&
|
||||||
|
node.Connected == "yes" &&
|
||||||
|
len(node.FailoverNode) > 0 &&
|
||||||
|
node.Failover != "yes" {
|
||||||
|
newMetrics.FailoverPeers[node.ID] = node.FailoverNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shouldUpdate := len(oldMetrics.FailoverPeers) == 0 && len(newMetrics.FailoverPeers) > 0
|
||||||
|
for k, v := range oldMetrics.FailoverPeers {
|
||||||
|
if len(newMetrics.FailoverPeers[k]) > 0 && len(v) == 0 {
|
||||||
|
shouldUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(v) > 0 && len(newMetrics.FailoverPeers[k]) == 0 {
|
||||||
|
newMetrics.FailoverPeers[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for k := range oldMetrics.Connectivity { // cleanup any left over data, self healing
|
for k := range oldMetrics.Connectivity { // cleanup any left over data, self healing
|
||||||
delete(newMetrics.Connectivity, k)
|
delete(newMetrics.Connectivity, k)
|
||||||
}
|
}
|
||||||
|
return shouldUpdate
|
||||||
}
|
}
|
||||||
|
|
50
mq/mq.go
50
mq/mq.go
|
@ -2,13 +2,13 @@ package mq
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
"github.com/gravitl/netmaker/logger"
|
"github.com/gravitl/netmaker/logger"
|
||||||
"github.com/gravitl/netmaker/netclient/ncutils"
|
"github.com/gravitl/netmaker/netclient/ncutils"
|
||||||
"github.com/gravitl/netmaker/servercfg"
|
"github.com/gravitl/netmaker/servercfg"
|
||||||
"github.com/gravitl/netmaker/serverctl"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// KEEPALIVE_TIMEOUT - time in seconds for timeout
|
// KEEPALIVE_TIMEOUT - time in seconds for timeout
|
||||||
|
@ -23,21 +23,57 @@ var peer_force_send = 0
|
||||||
|
|
||||||
var mqclient mqtt.Client
|
var mqclient mqtt.Client
|
||||||
|
|
||||||
// SetupMQTT creates a connection to broker and return client
|
// SetUpAdminClient - sets up admin client for the MQ
|
||||||
func SetupMQTT() {
|
func SetUpAdminClient() {
|
||||||
opts := mqtt.NewClientOptions()
|
opts := mqtt.NewClientOptions()
|
||||||
broker, secure := servercfg.GetMessageQueueEndpoint()
|
setMqOptions(mqAdminUserName, servercfg.GetMqAdminPassword(), opts)
|
||||||
|
mqAdminClient = mqtt.NewClient(opts)
|
||||||
|
opts.SetOnConnectHandler(func(client mqtt.Client) {
|
||||||
|
if token := client.Subscribe(dynamicSecSubTopic, 2, mqtt.MessageHandler(watchDynSecTopic)); token.WaitTimeout(MQ_TIMEOUT*time.Second) && token.Error() != nil {
|
||||||
|
client.Disconnect(240)
|
||||||
|
logger.Log(0, fmt.Sprintf("Dynamic security client subscription failed: %v ", token.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.SetOrderMatters(true)
|
||||||
|
opts.SetResumeSubs(true)
|
||||||
|
})
|
||||||
|
tperiod := time.Now().Add(10 * time.Second)
|
||||||
|
for {
|
||||||
|
if token := mqAdminClient.Connect(); !token.WaitTimeout(MQ_TIMEOUT*time.Second) || token.Error() != nil {
|
||||||
|
logger.Log(2, "Admin: unable to connect to broker, retrying ...")
|
||||||
|
if time.Now().After(tperiod) {
|
||||||
|
if token.Error() == nil {
|
||||||
|
logger.FatalLog("Admin: could not connect to broker, token timeout, exiting ...")
|
||||||
|
} else {
|
||||||
|
logger.FatalLog("Admin: could not connect to broker, exiting ...", token.Error().Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func setMqOptions(user, password string, opts *mqtt.ClientOptions) {
|
||||||
|
broker, _ := servercfg.GetMessageQueueEndpoint()
|
||||||
opts.AddBroker(broker)
|
opts.AddBroker(broker)
|
||||||
id := ncutils.MakeRandomString(23)
|
id := ncutils.MakeRandomString(23)
|
||||||
opts.ClientID = id
|
opts.ClientID = id
|
||||||
if secure {
|
opts.SetUsername(user)
|
||||||
opts.SetTLSConfig(&serverctl.TlsConfig)
|
opts.SetPassword(password)
|
||||||
}
|
|
||||||
opts.SetAutoReconnect(true)
|
opts.SetAutoReconnect(true)
|
||||||
opts.SetConnectRetry(true)
|
opts.SetConnectRetry(true)
|
||||||
opts.SetConnectRetryInterval(time.Second << 2)
|
opts.SetConnectRetryInterval(time.Second << 2)
|
||||||
opts.SetKeepAlive(time.Minute)
|
opts.SetKeepAlive(time.Minute)
|
||||||
opts.SetWriteTimeout(time.Minute)
|
opts.SetWriteTimeout(time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupMQTT creates a connection to broker and return client
|
||||||
|
func SetupMQTT() {
|
||||||
|
opts := mqtt.NewClientOptions()
|
||||||
|
setMqOptions(mqNetmakerServerUserName, servercfg.GetMqAdminPassword(), opts)
|
||||||
opts.SetOnConnectHandler(func(client mqtt.Client) {
|
opts.SetOnConnectHandler(func(client mqtt.Client) {
|
||||||
if token := client.Subscribe("ping/#", 2, mqtt.MessageHandler(Ping)); token.WaitTimeout(MQ_TIMEOUT*time.Second) && token.Error() != nil {
|
if token := client.Subscribe("ping/#", 2, mqtt.MessageHandler(Ping)); token.WaitTimeout(MQ_TIMEOUT*time.Second) && token.Error() != nil {
|
||||||
client.Disconnect(240)
|
client.Disconnect(240)
|
||||||
|
|
|
@ -33,23 +33,25 @@ func PublishPeerUpdate(newNode *models.Node, publishToSelf bool) error {
|
||||||
//skip self
|
//skip self
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
peerUpdate, err := logic.GetPeerUpdate(&node)
|
err = PublishSinglePeerUpdate(&node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(1, "error getting peer update for node", node.ID, err.Error())
|
logger.Log(1, "failed to publish peer update to node", node.Name, "on network", node.Network, ":", err.Error())
|
||||||
continue
|
|
||||||
}
|
|
||||||
data, err := json.Marshal(&peerUpdate)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log(2, "error marshaling peer update for node", node.ID, err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err = publish(&node, fmt.Sprintf("peers/%s/%s", node.Network, node.ID), data); err != nil {
|
|
||||||
logger.Log(1, "failed to publish peer update for node", node.ID)
|
|
||||||
} else {
|
|
||||||
logger.Log(1, "sent peer update for node", node.Name, "on network:", node.Network)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublishSinglePeerUpdate --- determines and publishes a peer update to one node
|
||||||
|
func PublishSinglePeerUpdate(node *models.Node) error {
|
||||||
|
peerUpdate, err := logic.GetPeerUpdate(node)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(&peerUpdate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return publish(node, fmt.Sprintf("peers/%s/%s", node.Network, node.ID), data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublishPeerUpdate --- publishes a peer update to all the peers of a node
|
// PublishPeerUpdate --- publishes a peer update to all the peers of a node
|
||||||
|
@ -184,7 +186,7 @@ func ServerStartNotify() error {
|
||||||
|
|
||||||
// function to collect and store metrics for server nodes
|
// function to collect and store metrics for server nodes
|
||||||
func collectServerMetrics(networks []models.Network) {
|
func collectServerMetrics(networks []models.Network) {
|
||||||
if !logic.Is_EE {
|
if !servercfg.Is_EE {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(networks) > 0 {
|
if len(networks) > 0 {
|
||||||
|
@ -217,9 +219,7 @@ func collectServerMetrics(networks []models.Network) {
|
||||||
logger.Log(2, "failed to push server metrics to exporter: ", err.Error())
|
logger.Log(2, "failed to push server metrics to exporter: ", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ func pushMetricsToExporter(metrics models.Metrics) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("failed to marshal metrics: " + err.Error())
|
return errors.New("failed to marshal metrics: " + err.Error())
|
||||||
}
|
}
|
||||||
if token := mqclient.Publish("metrics_exporter", 0, true, data); !token.WaitTimeout(MQ_TIMEOUT*time.Second) || token.Error() != nil {
|
if token := mqclient.Publish("metrics_exporter", 2, true, data); !token.WaitTimeout(MQ_TIMEOUT*time.Second) || token.Error() != nil {
|
||||||
var err error
|
var err error
|
||||||
if token.Error() == nil {
|
if token.Error() == nil {
|
||||||
err = errors.New("connection timeout")
|
err = errors.New("connection timeout")
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/rand"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -12,7 +10,6 @@ import (
|
||||||
"github.com/gravitl/netmaker/netclient/daemon"
|
"github.com/gravitl/netmaker/netclient/daemon"
|
||||||
"github.com/gravitl/netmaker/netclient/functions"
|
"github.com/gravitl/netmaker/netclient/functions"
|
||||||
"github.com/gravitl/netmaker/netclient/ncutils"
|
"github.com/gravitl/netmaker/netclient/ncutils"
|
||||||
"github.com/gravitl/netmaker/tls"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Join - join command to run from cli
|
// Join - join command to run from cli
|
||||||
|
@ -115,29 +112,8 @@ func Pull(cfg *config.ClientConfig) error {
|
||||||
|
|
||||||
currentServers[currCfg.Server.Server] = *currCfg
|
currentServers[currCfg.Server.Server] = *currCfg
|
||||||
}
|
}
|
||||||
//generate new client key if one doesn' exist
|
daemon.Restart()
|
||||||
var private *ed25519.PrivateKey
|
|
||||||
private, err = tls.ReadKeyFromFile(ncutils.GetNetclientPath() + ncutils.GetSeparator() + "client.key")
|
|
||||||
if err != nil {
|
|
||||||
_, newKey, err := ed25519.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := tls.SaveKeyToFile(ncutils.GetNetclientPath(), ncutils.GetSeparator()+"client.key", newKey); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
private = &newKey
|
|
||||||
}
|
|
||||||
// re-register with server -- get new certs for broker
|
|
||||||
for _, clientCfg := range currentServers {
|
|
||||||
if err = functions.RegisterWithServer(private, &clientCfg); err != nil {
|
|
||||||
logger.Log(0, "registration error", err.Error())
|
|
||||||
} else {
|
|
||||||
daemon.Restart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.Log(1, "reset network", cfg.Network, "and peer configs")
|
logger.Log(1, "reset network", cfg.Network, "and peer configs")
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,37 +192,10 @@ func LeaveNetwork(network string) error {
|
||||||
if err := removeHostDNS(cfg.Node.Interface, ncutils.IsWindows()); err != nil {
|
if err := removeHostDNS(cfg.Node.Interface, ncutils.IsWindows()); err != nil {
|
||||||
logger.Log(0, "failed to delete dns entries for", cfg.Node.Interface, err.Error())
|
logger.Log(0, "failed to delete dns entries for", cfg.Node.Interface, err.Error())
|
||||||
}
|
}
|
||||||
logger.Log(2, "deleting broker keys as required")
|
|
||||||
if !brokerInUse(cfg.Server.Server) {
|
|
||||||
if err := deleteBrokerFiles(cfg.Server.Server); err != nil {
|
|
||||||
logger.Log(0, "failed to deleter certs for", cfg.Server.Server, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.Log(2, "restarting daemon")
|
logger.Log(2, "restarting daemon")
|
||||||
return daemon.Restart()
|
return daemon.Restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
func brokerInUse(broker string) bool {
|
|
||||||
networks, _ := ncutils.GetSystemNetworks()
|
|
||||||
for _, net := range networks {
|
|
||||||
cfg := config.ClientConfig{}
|
|
||||||
cfg.Network = net
|
|
||||||
cfg.ReadConfig()
|
|
||||||
if cfg.Server.Server == broker {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteBrokerFiles(broker string) error {
|
|
||||||
dir := ncutils.GetNetclientServerPath(broker)
|
|
||||||
if err := os.RemoveAll(dir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteNodeFromServer(cfg *config.ClientConfig) error {
|
func deleteNodeFromServer(cfg *config.ClientConfig) error {
|
||||||
node := cfg.Node
|
node := cfg.Node
|
||||||
if node.IsServer == "yes" {
|
if node.IsServer == "yes" {
|
||||||
|
@ -340,6 +313,7 @@ func API(data any, method, url, authorization string) (*http.Response, error) {
|
||||||
if authorization != "" {
|
if authorization != "" {
|
||||||
request.Header.Set("authorization", "Bearer "+authorization)
|
request.Header.Set("authorization", "Bearer "+authorization)
|
||||||
}
|
}
|
||||||
|
request.Header.Set("requestfrom", "node")
|
||||||
return HTTPClient.Do(request)
|
return HTTPClient.Do(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,8 @@ package functions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -21,12 +16,10 @@ import (
|
||||||
"github.com/gravitl/netmaker/mq"
|
"github.com/gravitl/netmaker/mq"
|
||||||
"github.com/gravitl/netmaker/netclient/auth"
|
"github.com/gravitl/netmaker/netclient/auth"
|
||||||
"github.com/gravitl/netmaker/netclient/config"
|
"github.com/gravitl/netmaker/netclient/config"
|
||||||
"github.com/gravitl/netmaker/netclient/daemon"
|
|
||||||
"github.com/gravitl/netmaker/netclient/global_settings"
|
"github.com/gravitl/netmaker/netclient/global_settings"
|
||||||
"github.com/gravitl/netmaker/netclient/local"
|
"github.com/gravitl/netmaker/netclient/local"
|
||||||
"github.com/gravitl/netmaker/netclient/ncutils"
|
"github.com/gravitl/netmaker/netclient/ncutils"
|
||||||
"github.com/gravitl/netmaker/netclient/wireguard"
|
"github.com/gravitl/netmaker/netclient/wireguard"
|
||||||
ssl "github.com/gravitl/netmaker/tls"
|
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -73,12 +66,18 @@ func Daemon() error {
|
||||||
cancel()
|
cancel()
|
||||||
logger.Log(0, "shutting down netclient daemon")
|
logger.Log(0, "shutting down netclient daemon")
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
if mqclient != nil {
|
||||||
|
mqclient.Disconnect(250)
|
||||||
|
}
|
||||||
logger.Log(0, "shutdown complete")
|
logger.Log(0, "shutdown complete")
|
||||||
return nil
|
return nil
|
||||||
case <-reset:
|
case <-reset:
|
||||||
logger.Log(0, "received reset")
|
logger.Log(0, "received reset")
|
||||||
cancel()
|
cancel()
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
if mqclient != nil {
|
||||||
|
mqclient.Disconnect(250)
|
||||||
|
}
|
||||||
logger.Log(0, "restarting daemon")
|
logger.Log(0, "restarting daemon")
|
||||||
cancel = startGoRoutines(&wg)
|
cancel = startGoRoutines(&wg)
|
||||||
}
|
}
|
||||||
|
@ -94,11 +93,13 @@ func startGoRoutines(wg *sync.WaitGroup) context.CancelFunc {
|
||||||
cfg := config.ClientConfig{}
|
cfg := config.ClientConfig{}
|
||||||
cfg.Network = network
|
cfg.Network = network
|
||||||
cfg.ReadConfig()
|
cfg.ReadConfig()
|
||||||
if err := wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, ncutils.GetNetclientPathSpecific()+cfg.Node.Interface+".conf"); err != nil {
|
if cfg.Node.Connected == "yes" {
|
||||||
logger.Log(0, "failed to start ", cfg.Node.Interface, "wg interface", err.Error())
|
if err := wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, ncutils.GetNetclientPathSpecific()+cfg.Node.Interface+".conf"); err != nil {
|
||||||
}
|
logger.Log(0, "failed to start ", cfg.Node.Interface, "wg interface", err.Error())
|
||||||
if cfg.PublicIPService != "" {
|
}
|
||||||
global_settings.PublicIPServices[network] = cfg.PublicIPService
|
if cfg.PublicIPService != "" {
|
||||||
|
global_settings.PublicIPServices[network] = cfg.PublicIPService
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server := cfg.Server.Server
|
server := cfg.Server.Server
|
||||||
|
@ -201,47 +202,19 @@ func messageQueue(ctx context.Context, wg *sync.WaitGroup, cfg *config.ClientCon
|
||||||
logger.Log(0, "shutting down message queue for server", cfg.Server.Server)
|
logger.Log(0, "shutting down message queue for server", cfg.Server.Server)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTLSConf sets up tls configuration to connect to broker securely
|
|
||||||
func NewTLSConfig(server string) (*tls.Config, error) {
|
|
||||||
file := ncutils.GetNetclientServerPath(server) + ncutils.GetSeparator() + "root.pem"
|
|
||||||
certpool := x509.NewCertPool()
|
|
||||||
ca, err := os.ReadFile(file)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log(0, "could not read CA file", err.Error())
|
|
||||||
}
|
|
||||||
ok := certpool.AppendCertsFromPEM(ca)
|
|
||||||
if !ok {
|
|
||||||
logger.Log(0, "failed to append cert")
|
|
||||||
}
|
|
||||||
clientKeyPair, err := tls.LoadX509KeyPair(ncutils.GetNetclientServerPath(server)+ncutils.GetSeparator()+"client.pem", ncutils.GetNetclientPath()+ncutils.GetSeparator()+"client.key")
|
|
||||||
if err != nil {
|
|
||||||
logger.Log(0, "could not read client cert/key", err.Error())
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
certs := []tls.Certificate{clientKeyPair}
|
|
||||||
return &tls.Config{
|
|
||||||
RootCAs: certpool,
|
|
||||||
ClientAuth: tls.NoClientCert,
|
|
||||||
ClientCAs: nil,
|
|
||||||
Certificates: certs,
|
|
||||||
InsecureSkipVerify: false,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// func setMQTTSingenton creates a connection to broker for single use (ie to publish a message)
|
// func setMQTTSingenton creates a connection to broker for single use (ie to publish a message)
|
||||||
// only to be called from cli (eg. connect/disconnect, join, leave) and not from daemon ---
|
// only to be called from cli (eg. connect/disconnect, join, leave) and not from daemon ---
|
||||||
func setupMQTTSingleton(cfg *config.ClientConfig) error {
|
func setupMQTTSingleton(cfg *config.ClientConfig) error {
|
||||||
opts := mqtt.NewClientOptions()
|
opts := mqtt.NewClientOptions()
|
||||||
server := cfg.Server.Server
|
server := cfg.Server.Server
|
||||||
port := cfg.Server.MQPort
|
port := cfg.Server.MQPort
|
||||||
opts.AddBroker("ssl://" + server + ":" + port)
|
pass, err := os.ReadFile(ncutils.GetNetclientPathSpecific() + "secret-" + cfg.Network)
|
||||||
tlsConfig, err := NewTLSConfig(server)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(0, "failed to get TLS config for", server, err.Error())
|
return fmt.Errorf("could not read secrets file %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
opts.SetTLSConfig(tlsConfig)
|
opts.AddBroker("mqtts://" + server + ":" + port)
|
||||||
|
opts.SetUsername(cfg.Node.ID)
|
||||||
|
opts.SetPassword(string(pass))
|
||||||
mqclient = mqtt.NewClient(opts)
|
mqclient = mqtt.NewClient(opts)
|
||||||
var connecterr error
|
var connecterr error
|
||||||
opts.SetClientID(ncutils.MakeRandomString(23))
|
opts.SetClientID(ncutils.MakeRandomString(23))
|
||||||
|
@ -262,13 +235,13 @@ func setupMQTT(cfg *config.ClientConfig) error {
|
||||||
opts := mqtt.NewClientOptions()
|
opts := mqtt.NewClientOptions()
|
||||||
server := cfg.Server.Server
|
server := cfg.Server.Server
|
||||||
port := cfg.Server.MQPort
|
port := cfg.Server.MQPort
|
||||||
opts.AddBroker("ssl://" + server + ":" + port)
|
pass, err := os.ReadFile(ncutils.GetNetclientPathSpecific() + "secret-" + cfg.Network)
|
||||||
tlsConfig, err := NewTLSConfig(server)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(0, "failed to get TLS config for", server, err.Error())
|
return fmt.Errorf("could not read secrets file %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
opts.SetTLSConfig(tlsConfig)
|
opts.AddBroker(fmt.Sprintf("mqtts://%s:%s", server, port))
|
||||||
|
opts.SetUsername(cfg.Node.ID)
|
||||||
|
opts.SetPassword(string(pass))
|
||||||
opts.SetClientID(ncutils.MakeRandomString(23))
|
opts.SetClientID(ncutils.MakeRandomString(23))
|
||||||
opts.SetDefaultPublishHandler(All)
|
opts.SetDefaultPublishHandler(All)
|
||||||
opts.SetAutoReconnect(true)
|
opts.SetAutoReconnect(true)
|
||||||
|
@ -311,29 +284,13 @@ func setupMQTT(cfg *config.ClientConfig) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if connecterr != nil {
|
if connecterr != nil {
|
||||||
reRegisterWithServer(cfg)
|
logger.Log(0, "failed to establish connection to broker: ", connecterr.Error())
|
||||||
//try after re-registering
|
return connecterr
|
||||||
if token := mqclient.Connect(); !token.WaitTimeout(30*time.Second) || token.Error() != nil {
|
|
||||||
return errors.New("unable to connect to broker")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func reRegisterWithServer(cfg *config.ClientConfig) {
|
|
||||||
logger.Log(0, "connection issue detected.. attempt connection with new certs and broker information")
|
|
||||||
key, err := ssl.ReadKeyFromFile(ncutils.GetNetclientPath() + ncutils.GetSeparator() + "client.key")
|
|
||||||
if err != nil {
|
|
||||||
_, *key, err = ed25519.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("could not generate new key")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RegisterWithServer(key, cfg)
|
|
||||||
daemon.Restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
// publishes a message to server to update peers on this peer's behalf
|
// publishes a message to server to update peers on this peer's behalf
|
||||||
func publishSignal(nodeCfg *config.ClientConfig, signal byte) error {
|
func publishSignal(nodeCfg *config.ClientConfig, signal byte) error {
|
||||||
if err := publish(nodeCfg, fmt.Sprintf("signal/%s", nodeCfg.Node.ID), []byte{signal}, 1); err != nil {
|
if err := publish(nodeCfg, fmt.Sprintf("signal/%s", nodeCfg.Node.ID), []byte{signal}, 1); err != nil {
|
||||||
|
|
|
@ -199,7 +199,7 @@ func JoinNetwork(cfg *config.ClientConfig, privateKey string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if cfg.Node.Password == "" {
|
if cfg.Node.Password == "" {
|
||||||
cfg.Node.Password = logic.GenKey()
|
cfg.Node.Password = logic.GenPassWord()
|
||||||
}
|
}
|
||||||
//check if ListenPort was set on command line
|
//check if ListenPort was set on command line
|
||||||
if cfg.Node.ListenPort != 0 {
|
if cfg.Node.ListenPort != 0 {
|
||||||
|
@ -362,10 +362,6 @@ func JoinNetwork(cfg *config.ClientConfig, privateKey string) error {
|
||||||
|
|
||||||
local.SetNetmakerDomainRoute(cfg.Server.API)
|
local.SetNetmakerDomainRoute(cfg.Server.API)
|
||||||
cfg.Node = node
|
cfg.Node = node
|
||||||
if err := Register(cfg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Log(0, "starting wireguard")
|
logger.Log(0, "starting wireguard")
|
||||||
err = wireguard.InitWireguard(&node, privateKey, nodeGET.Peers[:])
|
err = wireguard.InitWireguard(&node, privateKey, nodeGET.Peers[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -20,7 +19,6 @@ import (
|
||||||
"github.com/gravitl/netmaker/netclient/auth"
|
"github.com/gravitl/netmaker/netclient/auth"
|
||||||
"github.com/gravitl/netmaker/netclient/config"
|
"github.com/gravitl/netmaker/netclient/config"
|
||||||
"github.com/gravitl/netmaker/netclient/ncutils"
|
"github.com/gravitl/netmaker/netclient/ncutils"
|
||||||
"github.com/gravitl/netmaker/tls"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var metricsCache = new(sync.Map)
|
var metricsCache = new(sync.Map)
|
||||||
|
@ -31,27 +29,25 @@ var metricsCache = new(sync.Map)
|
||||||
func Checkin(ctx context.Context, wg *sync.WaitGroup) {
|
func Checkin(ctx context.Context, wg *sync.WaitGroup) {
|
||||||
logger.Log(2, "starting checkin goroutine")
|
logger.Log(2, "starting checkin goroutine")
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
currentRun := 0
|
ticker := time.NewTicker(time.Minute * ncutils.CheckInInterval)
|
||||||
checkin(currentRun)
|
|
||||||
ticker := time.NewTicker(time.Second * 60)
|
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
logger.Log(0, "checkin routine closed")
|
logger.Log(0, "checkin routine closed")
|
||||||
return
|
return
|
||||||
//delay should be configuraable -> use cfg.Node.NetworkSettings.DefaultCheckInInterval ??
|
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
currentRun++
|
if mqclient != nil && mqclient.IsConnected() {
|
||||||
checkin(currentRun)
|
checkin()
|
||||||
if currentRun >= 5 {
|
} else {
|
||||||
currentRun = 0
|
logger.Log(0, "MQ client is not connected, skipping checkin...")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkin(currentRun int) {
|
func checkin() {
|
||||||
networks, _ := ncutils.GetSystemNetworks()
|
networks, _ := ncutils.GetSystemNetworks()
|
||||||
logger.Log(3, "checkin with server(s) for all networks")
|
logger.Log(3, "checkin with server(s) for all networks")
|
||||||
for _, network := range networks {
|
for _, network := range networks {
|
||||||
|
@ -69,41 +65,43 @@ func checkin(currentRun int) {
|
||||||
// defaults to iptables for now, may need another default for non-Linux OSes
|
// defaults to iptables for now, may need another default for non-Linux OSes
|
||||||
nodeCfg.Node.FirewallInUse = models.FIREWALL_IPTABLES
|
nodeCfg.Node.FirewallInUse = models.FIREWALL_IPTABLES
|
||||||
}
|
}
|
||||||
if nodeCfg.Node.IsStatic != "yes" {
|
if nodeCfg.Node.Connected == "yes" {
|
||||||
extIP, err := ncutils.GetPublicIP(nodeCfg.Server.API)
|
if nodeCfg.Node.IsStatic != "yes" {
|
||||||
if err != nil {
|
extIP, err := ncutils.GetPublicIP(nodeCfg.Server.API)
|
||||||
logger.Log(1, "error encountered checking public ip addresses: ", err.Error())
|
if err != nil {
|
||||||
}
|
logger.Log(1, "error encountered checking public ip addresses: ", err.Error())
|
||||||
if nodeCfg.Node.Endpoint != extIP && extIP != "" {
|
|
||||||
logger.Log(1, "network:", nodeCfg.Node.Network, "endpoint has changed from ", nodeCfg.Node.Endpoint, " to ", extIP)
|
|
||||||
nodeCfg.Node.Endpoint = extIP
|
|
||||||
if err := PublishNodeUpdate(&nodeCfg); err != nil {
|
|
||||||
logger.Log(0, "network:", nodeCfg.Node.Network, "could not publish endpoint change")
|
|
||||||
}
|
}
|
||||||
}
|
if nodeCfg.Node.Endpoint != extIP && extIP != "" {
|
||||||
intIP, err := getPrivateAddr()
|
logger.Log(1, "network:", nodeCfg.Node.Network, "endpoint has changed from ", nodeCfg.Node.Endpoint, " to ", extIP)
|
||||||
if err != nil {
|
nodeCfg.Node.Endpoint = extIP
|
||||||
logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking private ip addresses: ", err.Error())
|
if err := PublishNodeUpdate(&nodeCfg); err != nil {
|
||||||
}
|
logger.Log(0, "network:", nodeCfg.Node.Network, "could not publish endpoint change")
|
||||||
if nodeCfg.Node.LocalAddress != intIP && intIP != "" {
|
}
|
||||||
logger.Log(1, "network:", nodeCfg.Node.Network, "local Address has changed from ", nodeCfg.Node.LocalAddress, " to ", intIP)
|
|
||||||
nodeCfg.Node.LocalAddress = intIP
|
|
||||||
if err := PublishNodeUpdate(&nodeCfg); err != nil {
|
|
||||||
logger.Log(0, "Network: ", nodeCfg.Node.Network, " could not publish local address change")
|
|
||||||
}
|
}
|
||||||
}
|
intIP, err := getPrivateAddr()
|
||||||
_ = UpdateLocalListenPort(&nodeCfg)
|
if err != nil {
|
||||||
|
logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking private ip addresses: ", err.Error())
|
||||||
|
}
|
||||||
|
if nodeCfg.Node.LocalAddress != intIP && intIP != "" {
|
||||||
|
logger.Log(1, "network:", nodeCfg.Node.Network, "local Address has changed from ", nodeCfg.Node.LocalAddress, " to ", intIP)
|
||||||
|
nodeCfg.Node.LocalAddress = intIP
|
||||||
|
if err := PublishNodeUpdate(&nodeCfg); err != nil {
|
||||||
|
logger.Log(0, "Network: ", nodeCfg.Node.Network, " could not publish local address change")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = UpdateLocalListenPort(&nodeCfg)
|
||||||
|
|
||||||
} else if nodeCfg.Node.IsLocal == "yes" && nodeCfg.Node.LocalRange != "" {
|
} else if nodeCfg.Node.IsLocal == "yes" && nodeCfg.Node.LocalRange != "" {
|
||||||
localIP, err := ncutils.GetLocalIP(nodeCfg.Node.LocalRange)
|
localIP, err := ncutils.GetLocalIP(nodeCfg.Node.LocalRange)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking local ip addresses: ", err.Error())
|
logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking local ip addresses: ", err.Error())
|
||||||
}
|
}
|
||||||
if nodeCfg.Node.Endpoint != localIP && localIP != "" {
|
if nodeCfg.Node.Endpoint != localIP && localIP != "" {
|
||||||
logger.Log(1, "network:", nodeCfg.Node.Network, "endpoint has changed from "+nodeCfg.Node.Endpoint+" to ", localIP)
|
logger.Log(1, "network:", nodeCfg.Node.Network, "endpoint has changed from "+nodeCfg.Node.Endpoint+" to ", localIP)
|
||||||
nodeCfg.Node.Endpoint = localIP
|
nodeCfg.Node.Endpoint = localIP
|
||||||
if err := PublishNodeUpdate(&nodeCfg); err != nil {
|
if err := PublishNodeUpdate(&nodeCfg); err != nil {
|
||||||
logger.Log(0, "network:", nodeCfg.Node.Network, "could not publish localip change")
|
logger.Log(0, "network:", nodeCfg.Node.Network, "could not publish localip change")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,8 +111,7 @@ func checkin(currentRun int) {
|
||||||
config.Write(&nodeCfg, nodeCfg.Network)
|
config.Write(&nodeCfg, nodeCfg.Network)
|
||||||
}
|
}
|
||||||
Hello(&nodeCfg)
|
Hello(&nodeCfg)
|
||||||
checkCertExpiry(&nodeCfg)
|
if nodeCfg.Server.Is_EE && nodeCfg.Node.Connected == "yes" {
|
||||||
if currentRun >= 5 {
|
|
||||||
logger.Log(0, "collecting metrics for node", nodeCfg.Node.Name)
|
logger.Log(0, "collecting metrics for node", nodeCfg.Node.Name)
|
||||||
publishMetrics(&nodeCfg)
|
publishMetrics(&nodeCfg)
|
||||||
}
|
}
|
||||||
|
@ -266,22 +263,6 @@ func publish(nodeCfg *config.ClientConfig, dest string, msg []byte, qos byte) er
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCertExpiry(cfg *config.ClientConfig) error {
|
|
||||||
cert, err := tls.ReadCertFromFile(ncutils.GetNetclientServerPath(cfg.Server.Server) + ncutils.GetSeparator() + "client.pem")
|
|
||||||
//if cert doesn't exist or will expire within 10 days
|
|
||||||
if errors.Is(err, os.ErrNotExist) || cert.NotAfter.Before(time.Now().Add(time.Hour*24*10)) {
|
|
||||||
key, err := tls.ReadKeyFromFile(ncutils.GetNetclientPath() + ncutils.GetSeparator() + "client.key")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return RegisterWithServer(key, cfg)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkBroker(broker string, port string) error {
|
func checkBroker(broker string, port string) error {
|
||||||
if broker == "" {
|
if broker == "" {
|
||||||
return errors.New("error: broker address is blank")
|
return errors.New("error: broker address is blank")
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
package functions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gravitl/netmaker/logger"
|
|
||||||
"github.com/gravitl/netmaker/netclient/config"
|
|
||||||
"github.com/gravitl/netmaker/netclient/ncutils"
|
|
||||||
"github.com/gravitl/netmaker/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Register - the function responsible for registering with the server and acquiring certs
|
|
||||||
func Register(cfg *config.ClientConfig) error {
|
|
||||||
|
|
||||||
//generate new key if one doesn' exist
|
|
||||||
var private *ed25519.PrivateKey
|
|
||||||
var err error
|
|
||||||
private, err = tls.ReadKeyFromFile(ncutils.GetNetclientPath() + ncutils.GetSeparator() + "client.key")
|
|
||||||
if err != nil {
|
|
||||||
_, newKey, err := ed25519.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := tls.SaveKeyToFile(ncutils.GetNetclientPath(), ncutils.GetSeparator()+"client.key", newKey); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
private = &newKey
|
|
||||||
}
|
|
||||||
//check if cert exists
|
|
||||||
_, err = tls.ReadCertFromFile(ncutils.GetNetclientServerPath(cfg.Server.Server) + ncutils.GetSeparator() + "client.pem")
|
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
|
||||||
if err := RegisterWithServer(private, cfg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterWithServer calls the register endpoint with privatekey and commonname - api returns ca and client certificate
|
|
||||||
func RegisterWithServer(private *ed25519.PrivateKey, cfg *config.ClientConfig) error {
|
|
||||||
data := config.RegisterRequest{
|
|
||||||
Key: *private,
|
|
||||||
CommonName: tls.NewCName(cfg.Node.Name),
|
|
||||||
}
|
|
||||||
url := "https://" + cfg.Server.API + "/api/server/register"
|
|
||||||
logger.Log(1, "register at "+url)
|
|
||||||
|
|
||||||
token, err := Authenticate(cfg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
response, err := API(data, http.MethodPost, url, token)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if response.StatusCode != http.StatusOK {
|
|
||||||
return errors.New(response.Status)
|
|
||||||
}
|
|
||||||
var resp config.RegisterResponse
|
|
||||||
if err := json.NewDecoder(response.Body).Decode(&resp); err != nil {
|
|
||||||
return errors.New("unmarshal cert error " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// set broker information on register
|
|
||||||
var modServer bool
|
|
||||||
if resp.Broker != "" && resp.Broker != cfg.Server.Server {
|
|
||||||
cfg.Server.Server = resp.Broker
|
|
||||||
modServer = true
|
|
||||||
}
|
|
||||||
if resp.Port != "" && resp.Port != cfg.Server.MQPort {
|
|
||||||
cfg.Server.MQPort = resp.Port
|
|
||||||
modServer = true
|
|
||||||
}
|
|
||||||
if modServer {
|
|
||||||
if err = config.ModServerConfig(&cfg.Server, cfg.Node.Network); err != nil {
|
|
||||||
logger.Log(0, "network:", cfg.Node.Network, "error overwriting config with broker information: "+err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//x509.Certificate.PublicKey is an interface so json encoding/decoding results in a string rather that []byte
|
|
||||||
//the pubkeys are included in the response so the values in the certificate can be updated appropriately
|
|
||||||
resp.CA.PublicKey = resp.CAPubKey
|
|
||||||
resp.Cert.PublicKey = resp.CertPubKey
|
|
||||||
if err := tls.SaveCertToFile(ncutils.GetNetclientServerPath(cfg.Server.Server)+ncutils.GetSeparator(), tls.ROOT_PEM_NAME, &resp.CA); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := tls.SaveCertToFile(ncutils.GetNetclientServerPath(cfg.Server.Server)+ncutils.GetSeparator(), "client.pem", &resp.Cert); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Log(0, "network:", cfg.Network, "certificates/key saved ")
|
|
||||||
//join the network defined in the token
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -6,6 +6,7 @@ func InitializeUpgrades() {
|
||||||
upgrade0145,
|
upgrade0145,
|
||||||
upgrade0146,
|
upgrade0146,
|
||||||
upgrade0160,
|
upgrade0160,
|
||||||
|
upgrade0161,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
netclient/functions/upgrades/v0-16-1.go
Normal file
24
netclient/functions/upgrades/v0-16-1.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package upgrades
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gravitl/netmaker/netclient/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
var upgrade0161 = UpgradeInfo{
|
||||||
|
RequiredVersions: []string{
|
||||||
|
"v0.14.6",
|
||||||
|
"v0.15.0",
|
||||||
|
"v0.15.1",
|
||||||
|
"v0.15.2",
|
||||||
|
"v0.16.1",
|
||||||
|
},
|
||||||
|
NewVersion: "v0.16.1",
|
||||||
|
OP: update0161,
|
||||||
|
}
|
||||||
|
|
||||||
|
func update0161(cfg *config.ClientConfig) {
|
||||||
|
// set connect default if not present 15.X -> 16.0
|
||||||
|
if cfg.Node.Connected == "" {
|
||||||
|
cfg.Node.SetDefaultConnected()
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,9 @@ import (
|
||||||
"github.com/gravitl/netmaker/logger"
|
"github.com/gravitl/netmaker/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CheckInInterval - the interval for check-in time in units/minute
|
||||||
|
const CheckInInterval = 1
|
||||||
|
|
||||||
// BackOff - back off any function while there is an error
|
// BackOff - back off any function while there is an error
|
||||||
func BackOff(isExponential bool, maxTime int, f interface{}) (interface{}, error) {
|
func BackOff(isExponential bool, maxTime int, f interface{}) (interface{}, error) {
|
||||||
// maxTime seconds
|
// maxTime seconds
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
<assemblyIdentity
|
<assemblyIdentity
|
||||||
version="0.16.0.0"
|
version="0.16.1.0"
|
||||||
processorArchitecture="*"
|
processorArchitecture="*"
|
||||||
name="netclient.exe"
|
name="netclient.exe"
|
||||||
type="win32"
|
type="win32"
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
"FileVersion": {
|
"FileVersion": {
|
||||||
"Major": 0,
|
"Major": 0,
|
||||||
"Minor": 16,
|
"Minor": 16,
|
||||||
"Patch": 0,
|
"Patch": 1,
|
||||||
"Build": 0
|
"Build": 0
|
||||||
},
|
},
|
||||||
"ProductVersion": {
|
"ProductVersion": {
|
||||||
"Major": 0,
|
"Major": 0,
|
||||||
"Minor": 16,
|
"Minor": 16,
|
||||||
"Patch": 0,
|
"Patch": 1,
|
||||||
"Build": 0
|
"Build": 0
|
||||||
},
|
},
|
||||||
"FileFlagsMask": "3f",
|
"FileFlagsMask": "3f",
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
"OriginalFilename": "",
|
"OriginalFilename": "",
|
||||||
"PrivateBuild": "",
|
"PrivateBuild": "",
|
||||||
"ProductName": "Netclient",
|
"ProductName": "Netclient",
|
||||||
"ProductVersion": "v0.16.0.0",
|
"ProductVersion": "v0.16.1.0",
|
||||||
"SpecialBuild": ""
|
"SpecialBuild": ""
|
||||||
},
|
},
|
||||||
"VarFileInfo": {
|
"VarFileInfo": {
|
||||||
|
|
|
@ -26,7 +26,7 @@ func WgQuickDownMac(node *models.Node, iface string) error {
|
||||||
|
|
||||||
// RemoveConfMac - bring down mac interface and remove routes
|
// RemoveConfMac - bring down mac interface and remove routes
|
||||||
func RemoveConfMac(iface string) error {
|
func RemoveConfMac(iface string) error {
|
||||||
realIface, err := getRealIface(iface)
|
realIface, err := GetRealIface(iface)
|
||||||
if realIface != "" {
|
if realIface != "" {
|
||||||
err = deleteInterface(iface, realIface)
|
err = deleteInterface(iface, realIface)
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ func RemoveConfMac(iface string) error {
|
||||||
func WgQuickUpMac(node *models.Node, iface string, confPath string) error {
|
func WgQuickUpMac(node *models.Node, iface string, confPath string) error {
|
||||||
var err error
|
var err error
|
||||||
var realIface string
|
var realIface string
|
||||||
realIface, err = getRealIface(iface)
|
realIface, err = GetRealIface(iface)
|
||||||
if realIface != "" && err == nil {
|
if realIface != "" && err == nil {
|
||||||
deleteInterface(iface, realIface)
|
deleteInterface(iface, realIface)
|
||||||
deleteRoutes(realIface)
|
deleteRoutes(realIface)
|
||||||
|
@ -101,8 +101,8 @@ func addInterface(iface string) (string, error) {
|
||||||
return realIface, err
|
return realIface, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRealIface - retrieves tun iface based on reference iface name from config file
|
// GetRealIface - retrieves tun iface based on reference iface name from config file
|
||||||
func getRealIface(iface string) (string, error) {
|
func GetRealIface(iface string) (string, error) {
|
||||||
ncutils.RunCmd("wg show interfaces", false)
|
ncutils.RunCmd("wg show interfaces", false)
|
||||||
ifacePath := "/var/run/wireguard/" + iface + ".name"
|
ifacePath := "/var/run/wireguard/" + iface + ".name"
|
||||||
if !(ncutils.FileExists(ifacePath)) {
|
if !(ncutils.FileExists(ifacePath)) {
|
||||||
|
@ -120,7 +120,7 @@ func getRealIface(iface string) (string, error) {
|
||||||
|
|
||||||
// deleteRoutes - deletes network routes associated with interface
|
// deleteRoutes - deletes network routes associated with interface
|
||||||
func deleteRoutes(iface string) error {
|
func deleteRoutes(iface string) error {
|
||||||
realIface, err := getRealIface(iface)
|
realIface, err := GetRealIface(iface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ if [ "$TOKEN" != "" ]; then
|
||||||
TOKEN_CMD="-t $TOKEN"
|
TOKEN_CMD="-t $TOKEN"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
/root/netclient join $TOKEN_CMD -dnson no -udpholepunch no
|
/root/netclient join $TOKEN_CMD -udpholepunch no
|
||||||
if [ $? -ne 0 ]; then { echo "Failed to join, quitting." ; exit 1; } fi
|
if [ $? -ne 0 ]; then { echo "Failed to join, quitting." ; exit 1; } fi
|
||||||
|
|
||||||
echo "[netclient] Starting netclient daemon"
|
echo "[netclient] Starting netclient daemon"
|
||||||
|
|
|
@ -80,7 +80,7 @@ COREDNS_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')
|
||||||
SERVER_PUBLIC_IP=$(curl -s ifconfig.me)
|
SERVER_PUBLIC_IP=$(curl -s ifconfig.me)
|
||||||
MASTER_KEY=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo '')
|
MASTER_KEY=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo '')
|
||||||
EMAIL="$(echo $RANDOM | md5sum | head -c 32)@email.com"
|
EMAIL="$(echo $RANDOM | md5sum | head -c 32)@email.com"
|
||||||
|
MQ_ADMIN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 64 ; echo '')
|
||||||
if [ -n "$domain" ]; then
|
if [ -n "$domain" ]; then
|
||||||
NETMAKER_BASE_DOMAIN=$domain
|
NETMAKER_BASE_DOMAIN=$domain
|
||||||
fi
|
fi
|
||||||
|
@ -128,7 +128,8 @@ sleep 5
|
||||||
echo "setting mosquitto.conf..."
|
echo "setting mosquitto.conf..."
|
||||||
|
|
||||||
wget -q -O /root/mosquitto.conf https://raw.githubusercontent.com/gravitl/netmaker/master/docker/mosquitto.conf
|
wget -q -O /root/mosquitto.conf https://raw.githubusercontent.com/gravitl/netmaker/master/docker/mosquitto.conf
|
||||||
|
wget -q -O /root/wait.sh https://raw.githubusercontent.com/gravitl/netmaker/master/docker/wait.sh
|
||||||
|
chmod +x /root/wait.sh
|
||||||
echo "setting docker-compose..."
|
echo "setting docker-compose..."
|
||||||
|
|
||||||
mkdir -p /etc/netmaker
|
mkdir -p /etc/netmaker
|
||||||
|
@ -139,7 +140,7 @@ sed -i "s/SERVER_PUBLIC_IP/$SERVER_PUBLIC_IP/g" /root/docker-compose.yml
|
||||||
sed -i "s/COREDNS_IP/$COREDNS_IP/g" /root/docker-compose.yml
|
sed -i "s/COREDNS_IP/$COREDNS_IP/g" /root/docker-compose.yml
|
||||||
sed -i "s/REPLACE_MASTER_KEY/$MASTER_KEY/g" /root/docker-compose.yml
|
sed -i "s/REPLACE_MASTER_KEY/$MASTER_KEY/g" /root/docker-compose.yml
|
||||||
sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/docker-compose.yml
|
sed -i "s/YOUR_EMAIL/$EMAIL/g" /root/docker-compose.yml
|
||||||
|
sed -i "s/REPLACE_MQ_ADMIN_PASSWORD/$MQ_ADMIN_PASSWORD/g" /root/docker-compose.yml
|
||||||
echo "starting containers..."
|
echo "starting containers..."
|
||||||
|
|
||||||
docker-compose -f /root/docker-compose.yml up -d
|
docker-compose -f /root/docker-compose.yml up -d
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Version = "dev"
|
Version = "dev"
|
||||||
|
Is_EE = false
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetHost - sets the host ip
|
// SetHost - sets the host ip
|
||||||
|
@ -84,6 +85,10 @@ func GetServerConfig() config.ServerConfig {
|
||||||
cfg.PortForwardServices = services
|
cfg.PortForwardServices = services
|
||||||
cfg.Server = GetServer()
|
cfg.Server = GetServer()
|
||||||
cfg.Verbosity = GetVerbosity()
|
cfg.Verbosity = GetVerbosity()
|
||||||
|
cfg.IsEE = "no"
|
||||||
|
if Is_EE {
|
||||||
|
cfg.IsEE = "yes"
|
||||||
|
}
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
@ -101,6 +106,7 @@ func GetServerInfo() models.ServerConfig {
|
||||||
}
|
}
|
||||||
cfg.Version = GetVersion()
|
cfg.Version = GetVersion()
|
||||||
cfg.Server = GetServer()
|
cfg.Server = GetServer()
|
||||||
|
cfg.Is_EE = Is_EE
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
@ -616,6 +622,17 @@ func GetMQServerPort() string {
|
||||||
return port
|
return port
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMqAdminPassword - fetches the MQ Admin password
|
||||||
|
func GetMqAdminPassword() string {
|
||||||
|
password := ""
|
||||||
|
if os.Getenv("MQ_ADMIN_PASSWORD") != "" {
|
||||||
|
password = os.Getenv("MQ_ADMIN_PASSWORD")
|
||||||
|
} else if config.Config.Server.MQAdminPassword != "" {
|
||||||
|
password = config.Config.Server.MQAdminPassword
|
||||||
|
}
|
||||||
|
return password
|
||||||
|
}
|
||||||
|
|
||||||
// IsBasicAuthEnabled - checks if basic auth has been configured to be turned off
|
// IsBasicAuthEnabled - checks if basic auth has been configured to be turned off
|
||||||
func IsBasicAuthEnabled() bool {
|
func IsBasicAuthEnabled() bool {
|
||||||
var enabled = true //default
|
var enabled = true //default
|
||||||
|
|
|
@ -132,9 +132,32 @@ func setNetworkDefaults() error {
|
||||||
logger.Log(0, "could not initialize NetworkUsers on network", net.NetID)
|
logger.Log(0, "could not initialize NetworkUsers on network", net.NetID)
|
||||||
}
|
}
|
||||||
pro.AddProNetDefaults(&net)
|
pro.AddProNetDefaults(&net)
|
||||||
_, _, _, _, _, _, err = logic.UpdateNetwork(&net, &net)
|
update := false
|
||||||
if err != nil {
|
newNet := net
|
||||||
logger.Log(0, "could not set defaults on network", net.NetID)
|
if strings.Contains(net.NetID, ".") {
|
||||||
|
newNet.NetID = strings.ReplaceAll(net.NetID, ".", "")
|
||||||
|
newNet.DefaultInterface = strings.ReplaceAll(net.DefaultInterface, ".", "")
|
||||||
|
update = true
|
||||||
|
}
|
||||||
|
if strings.ContainsAny(net.NetID, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") {
|
||||||
|
newNet.NetID = strings.ToLower(net.NetID)
|
||||||
|
newNet.DefaultInterface = strings.ToLower(net.DefaultInterface)
|
||||||
|
update = true
|
||||||
|
}
|
||||||
|
if update {
|
||||||
|
newNet.SetDefaults()
|
||||||
|
if err := logic.SaveNetwork(&newNet); err != nil {
|
||||||
|
logger.Log(0, "error saving networks during initial update:", err.Error())
|
||||||
|
}
|
||||||
|
if err := logic.DeleteNetwork(net.NetID); err != nil {
|
||||||
|
logger.Log(0, "error deleting old network:", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
net.SetDefaults()
|
||||||
|
_, _, _, _, _, _, err = logic.UpdateNetwork(&net, &net)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log(0, "could not set defaults on network", net.NetID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -746,7 +746,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.16.0
|
version: 0.16.1
|
||||||
paths:
|
paths:
|
||||||
/api/dns:
|
/api/dns:
|
||||||
get:
|
get:
|
||||||
|
|
|
@ -3,7 +3,7 @@ package validation
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
validator "github.com/go-playground/validator/v10"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckYesOrNo - checks if a field on a struct is yes or no
|
// CheckYesOrNo - checks if a field on a struct is yes or no
|
||||||
|
|
Loading…
Reference in a new issue