├── .dockerignore ├── .fpm ├── .github ├── ISSUE_TEMPLATE │ └── bug-report.yml ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── branchtest.yml │ ├── deletedroplets.yml │ ├── docker-builder.yml │ ├── docs.yml │ ├── pre-release.yml │ ├── publish-docker.yml │ ├── release.yml │ ├── sendtokitemaker.yml │ └── test.yml ├── .gitignore ├── .goreleaser.prerelease.yaml ├── .goreleaser.update.yaml ├── .goreleaser.yaml ├── .swaggo ├── CONTRIBUTING.md ├── Dockerfile ├── Dockerfile-quick ├── LICENSE.md ├── README.md ├── SECURITY.md ├── auth ├── auth.go └── host_session.go ├── cli ├── cmd │ ├── acl │ │ ├── allow.go │ │ ├── deny.go │ │ ├── list.go │ │ └── root.go │ ├── commons │ │ └── globals.go │ ├── context │ │ ├── delete.go │ │ ├── list.go │ │ ├── root.go │ │ ├── set.go │ │ └── use.go │ ├── dns │ │ ├── create.go │ │ ├── delete.go │ │ ├── flags.go │ │ ├── list.go │ │ ├── push.go │ │ └── root.go │ ├── enrollment_key │ │ ├── create.go │ │ ├── delete.go │ │ ├── list.go │ │ └── root.go │ ├── ext_client │ │ ├── config.go │ │ ├── create.go │ │ ├── delete.go │ │ ├── get.go │ │ ├── list.go │ │ ├── root.go │ │ └── update.go │ ├── failover │ │ ├── disable.go │ │ ├── enable.go │ │ └── root.go │ ├── gateway │ │ ├── create.go │ │ ├── delete.go │ │ └── root.go │ ├── host │ │ ├── add_network.go │ │ ├── delete.go │ │ ├── delete_network.go │ │ ├── list.go │ │ ├── refresh_keys.go │ │ ├── root.go │ │ └── update.go │ ├── logs.go │ ├── metrics │ │ ├── all.go │ │ ├── network.go │ │ ├── network_ext.go │ │ ├── node.go │ │ └── root.go │ ├── network │ │ ├── create.go │ │ ├── delete.go │ │ ├── flags.go │ │ ├── get.go │ │ ├── list.go │ │ ├── node_limit.go │ │ └── root.go │ ├── node │ │ ├── create_egress.go │ │ ├── create_ingress.go │ │ ├── create_relay.go │ │ ├── delete.go │ │ ├── delete_egress.go │ │ ├── delete_ingress.go │ │ ├── delete_relay.go │ │ ├── flags.go │ │ ├── get.go │ │ ├── list.go │ │ ├── root.go │ │ └── update.go │ ├── root.go │ ├── server │ │ ├── config.go │ │ ├── has_admin.go │ │ ├── health.go │ │ ├── info.go │ │ └── root.go │ └── user │ │ ├── create.go │ │ ├── delete.go │ │ ├── flags.go │ │ ├── get.go │ │ ├── groups.go │ │ ├── list.go │ │ ├── roles.go │ │ ├── root.go │ │ └── update.go ├── config │ └── config.go ├── functions │ ├── acl.go │ ├── dns.go │ ├── enrollment_keys.go │ ├── ext_client.go │ ├── failover.go │ ├── gateway.go │ ├── host.go │ ├── http_client.go │ ├── metrics.go │ ├── network.go │ ├── node.go │ ├── pretty_print.go │ ├── server.go │ └── user.go ├── main.go └── samples │ ├── network.json │ └── node.json ├── compose ├── docker-compose-emqx.yml ├── docker-compose.netclient.yml ├── docker-compose.pro.yml └── docker-compose.yml ├── config ├── config.go ├── config_test.go └── environments │ └── dev.yaml ├── controllers ├── acls.go ├── config │ ├── dnsconfig │ │ └── Corefile │ └── environments │ │ └── dev.yaml ├── controller.go ├── debug.go ├── dns.go ├── dns_test.go ├── enrollmentkeys.go ├── ext_client.go ├── files.go ├── gateway.go ├── hosts.go ├── ipservice.go ├── legacy.go ├── limits.go ├── logger.go ├── middleware.go ├── migrate.go ├── network.go ├── network_test.go ├── node.go ├── node_test.go ├── regex.go ├── regex_test.go ├── response_test.go ├── server.go ├── tags.go ├── test.db ├── user.go └── user_test.go ├── database ├── database.go ├── postgres.go ├── rqlite.go ├── sqlite.go └── statics.go ├── dev.yaml ├── docker ├── Caddyfile ├── Caddyfile-pro ├── Dockerfile-go-builder ├── Dockerfile-netclient-doks ├── Dockerfile-netclient-doks-uspace ├── Dockerfile-netclient-multiarch ├── Dockerfile-netclient-multiarch-userspace ├── emqx.conf ├── mosquitto.conf └── wait.sh ├── docs ├── APIUsage.md ├── Authentication.md └── Pricing.md ├── functions ├── helpers.go ├── helpers_test.go └── local.go ├── go.mod ├── go.sum ├── k8s ├── client │ ├── netclient-daemonset.yaml │ └── netclient.yaml ├── misc │ ├── clusterissuer.yaml │ ├── dnsutils.yaml │ ├── nginx-example.yaml │ ├── pingtest.yaml │ └── ubuntu.yaml └── server │ ├── README.md │ ├── mosquitto.yaml │ ├── netmaker-server.yaml │ └── netmaker-ui.yaml ├── logger ├── logger.go ├── types.go └── util.go ├── logic ├── acls.go ├── acls │ ├── common.go │ ├── nodeacls │ │ ├── modify.go │ │ ├── retrieve.go │ │ └── types.go │ └── types.go ├── auth.go ├── clients.go ├── dns.go ├── enrollmentkey.go ├── enrollmentkey_test.go ├── errors.go ├── extpeers.go ├── gateway.go ├── host_test.go ├── hostactions │ └── hostactions.go ├── hosts.go ├── jwts.go ├── legacy.go ├── metrics.go ├── networks.go ├── nodes.go ├── nodes_test.go ├── peers.go ├── pro │ └── netcache │ │ └── netcache.go ├── proc.go ├── relay.go ├── security.go ├── server.go ├── serverconf.go ├── status.go ├── tags.go ├── telemetry.go ├── timer.go ├── traffic.go ├── user_mgmt.go ├── users.go ├── util.go ├── util_test.go ├── version.go ├── version_test.go ├── wireguard.go └── zombie.go ├── main.go ├── main_ee.go ├── migrate └── migrate.go ├── models ├── accessToken.go ├── acl.go ├── api_host.go ├── api_node.go ├── dnsEntry.go ├── enrollment_key.go ├── error.go ├── extclient.go ├── gateway.go ├── host.go ├── intclient.go ├── metrics.go ├── migrate.go ├── mqtt.go ├── names.go ├── network.go ├── network_test.go ├── node.go ├── ssocache.go ├── structs.go ├── tags.go └── user_mgmt.go ├── mq ├── emqx.go ├── emqx_cloud.go ├── emqx_on_prem.go ├── handlers.go ├── migrate.go ├── mq.go ├── publishers.go └── util.go ├── netclient └── ncutils │ ├── constants.go │ ├── encryption.go │ ├── iface.go │ ├── netclientutils.go │ └── util.go ├── nginx └── netmaker-nginx-template.conf ├── pro ├── LICENSE ├── auth │ ├── auth.go │ ├── azure-ad.go │ ├── error.go │ ├── github.go │ ├── google.go │ ├── headless_callback.go │ ├── oidc.go │ ├── register_callback.go │ └── templates.go ├── controllers │ ├── failover.go │ ├── inet_gws.go │ ├── metrics.go │ ├── middleware.go │ ├── rac.go │ └── users.go ├── email │ ├── email.go │ ├── invite.go │ ├── smtp.go │ └── utils.go ├── initialize.go ├── license.go ├── license_test.go ├── logic │ ├── ext_acls.go │ ├── failover.go │ ├── metrics.go │ ├── migrate.go │ ├── nodes.go │ ├── security.go │ ├── status.go │ └── user_mgmt.go ├── remote_access_client.go ├── trial.go ├── types.go └── util.go ├── release.md ├── scripts ├── build-binaries.sh ├── netclient-rc-freebsd ├── netclient.sh ├── netmaker.default.env ├── nm-quick.sh ├── nm-upgrade-0-17-1-to-0-19-0.sh ├── nm-upgrade.sh ├── openwrt-daemon-2.sh ├── openwrt-daemon.sh ├── token-convert.sh └── userspace-entrypoint.sh ├── servercfg ├── serverconf.go ├── serverconf_test.go └── sqlconf.go ├── serverctl ├── serverctl.go └── tls.go ├── swagger.yaml ├── test ├── admincreate.sh ├── api_test.go.bak ├── config │ ├── dnsconfig │ │ ├── Corefile │ │ └── netmaker.hosts │ └── environments │ │ └── dev.yaml ├── gatewaycreate.sh ├── groupcreate.sh ├── groupscreate.sh ├── keycreate.sh ├── network_test.go.bak ├── node_test.go.bak ├── nodecreate.sh ├── nodescreate.sh ├── test.script └── user_test.go.bak ├── tls └── tls.go ├── utils └── utils.go └── validation └── validation.go /.dockerignore: -------------------------------------------------------------------------------- 1 | config/dnsconfig/ 2 | data/ 3 | /.git 4 | /*.tar -------------------------------------------------------------------------------- /.fpm: -------------------------------------------------------------------------------- 1 | --name netclient 2 | --license sspl 3 | --depends wireguard-tools 4 | --description "Netmaker's netclient agent and CLI" 5 | --url "https//:github.com/gravitl/netmaker" 6 | --maintainer "info@gravitl.com" 7 | --vendor Gravitl 8 | 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Basic dependabot.yml file with minimum configuration for gomod. 2 | 3 | version: 2 4 | updates: 5 | # Enable version updates for netmaker 6 | - package-ecosystem: "gomod" 7 | directory: "/" 8 | # Check for updates every day (weekdays) 9 | schedule: 10 | interval: "weekly" 11 | target-branch: "develop" 12 | # Enable version updates for netclient 13 | - package-ecosystem: "gomod" 14 | directory: "/netclient" 15 | # Check for updates every day (weekdays) 16 | schedule: 17 | interval: "weekly" 18 | target-branch: "develop" 19 | # Enable version updates for GitHubActions 20 | - package-ecosystem: "github-actions" 21 | directory: "/" 22 | schedule: 23 | interval: "weekly" 24 | target-branch: "develop" 25 | # Enable version updates for docker images 26 | - package-ecosystem: "docker" 27 | directory: "/" 28 | schedule: 29 | interval: "weekly" 30 | target-branch: "develop" 31 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Describe your changes 2 | 3 | ## Provide Issue ticket number if applicable/not in title 4 | 5 | ## Provide testing steps 6 | 7 | ## Checklist before requesting a review 8 | - [ ] My changes affect only 10 files or less. 9 | - [ ] I have performed a self-review of my code and tested it. 10 | - [ ] If it is a new feature, I have added thorough tests, my code is <= 1450 lines. 11 | - [ ] If it is a bugfix, my code is <= 200 lines. 12 | - [ ] My functions are <= 80 lines. 13 | - [ ] I have had my code reviewed by a peer. 14 | - [ ] My unit tests pass locally. 15 | - [ ] Netmaker is awesome. 16 | -------------------------------------------------------------------------------- /.github/workflows/docker-builder.yml: -------------------------------------------------------------------------------- 1 | name: Build go-builder images 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | docker_tag: 7 | description: 'Docker tag to use (default: latest)' 8 | required: false 9 | default: 'latest' 10 | jobs: 11 | go-builder: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | - name: SetUp Buildx 17 | uses: docker/setup-buildx-action@v3 18 | - name: Login to Dockerhub 19 | uses: docker/login-action@v3 20 | with: 21 | username: ${{ secrets.DOCKERHUB_USERNAME }} 22 | password: ${{ secrets.DOCKERHUB_TOKEN }} 23 | - name: Build and push to docker hub 24 | uses: docker/build-push-action@v6 25 | with: 26 | context: . 27 | push: true 28 | platforms: linux/amd64, linux/arm64, linux/arm/v7 29 | file: ./docker/Dockerfile-go-builder 30 | tags: gravitl/go-builder:${{ github.event.inputs.docker_tag || 'latest' }} 31 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Generate Documentation 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | branch: 7 | description: 'Branch to run the workflow against' 8 | required: true 9 | default: 'master' 10 | 11 | jobs: 12 | generate-docs: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | repository: gravitl/netmaker 19 | ref: ${{ github.event.inputs.branch || 'master' }} 20 | 21 | - name: Setup Go 22 | uses: actions/setup-go@v5 23 | with: 24 | go-version-file: go.mod 25 | 26 | - name: Install Swag 27 | run: go install github.com/swaggo/swag/cmd/swag@latest 28 | 29 | - name: Generating Docs 30 | run: | 31 | export PATH=$PATH:$(go env GOPATH)/bin 32 | swag i --md docs/ --parseDependency --parseInternal --outputTypes yaml --parseDepth 1 --output . 33 | 34 | - name: Get current timestamp 35 | id: timestamp 36 | run: echo "timestamp=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_OUTPUT 37 | 38 | - name: Create Pull Request 39 | uses: peter-evans/create-pull-request@v7 40 | with: 41 | token: ${{ secrets.GITHUB_TOKEN }} 42 | commit-message: "Update documentation ${{ steps.timestamp.outputs.timestamp }}" 43 | title: "Update Swagger documentation ${{ steps.timestamp.outputs.timestamp }}" 44 | body: | 45 | This PR updates the swagger.yml file with the latest documentation changes. 46 | 47 | Updated on: ${{ steps.timestamp.outputs.timestamp }} 48 | branch: update-swagger-docs-${{ github.event.inputs.branch }} 49 | base: ${{ github.event.inputs.branch }} 50 | delete-branch: true 51 | -------------------------------------------------------------------------------- /.github/workflows/pre-release.yml: -------------------------------------------------------------------------------- 1 | # creates a release from develop 2 | # creates release branch, generates release assets, publishes docker image and copies release.md to release 3 | # linux packages are generated and a PR from release branch to master is created 4 | name: Release 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | version: 10 | description: "new version number" 11 | required: true 12 | 13 | jobs: 14 | 15 | release: 16 | uses: gravitl/devops/.github/workflows/netmakerPrerelease.yml@master 17 | with: 18 | version: ${{ inputs.version }} 19 | secrets: inherit 20 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # creates a release from develop 2 | # creates release branch, generates release assets, publishes docker image and copies release.md to release 3 | # linux packages are generated and a PR from release branch to master is created 4 | name: Release 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | version: 10 | description: "new version number" 11 | required: true 12 | 13 | jobs: 14 | 15 | release: 16 | uses: gravitl/devops/.github/workflows/netmakerRelease.yml@master 17 | with: 18 | version: ${{ inputs.version }} 19 | secrets: inherit 20 | -------------------------------------------------------------------------------- /.github/workflows/sendtokitemaker.yml: -------------------------------------------------------------------------------- 1 | name: Send to Kitemaker 2 | 3 | on: 4 | issues: 5 | types: opened 6 | 7 | 8 | jobs: 9 | send-ticket: 10 | uses: gravitl/devops/.github/workflows/makekitemakerticket.yml@master 11 | with: 12 | title: ${{ github.event.issue.title }} 13 | body: ${{ github.event.issue.body }} 14 | repo_name: ${{ github.event.repository.name }} 15 | secrets: inherit 16 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Integration Test 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | types: [opened, synchronize, reopened] 7 | 8 | jobs: 9 | 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | - name: Setup Go 16 | uses: actions/setup-go@v5 17 | with: 18 | go-version-file: 'go.mod' 19 | - name: Build 20 | run: | 21 | env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build main.go 22 | env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags=ee main.go 23 | 24 | nmctl: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | - name: Setup go 30 | uses: actions/setup-go@v5 31 | with: 32 | go-version-file: 'go.mod' 33 | - name: Build 34 | run: | 35 | cd cli 36 | GOOS=linux GOARCH=amd64 go build -o nmctl 37 | GOOS=darwin GOARCH=amd64 go build -o nmctl 38 | GOOS=darwin GOARCH=arm64 go build -o nmctl 39 | GOOS=windows GOARCH=amd64 go build -o nmctl 40 | 41 | tests: 42 | runs-on: ubuntu-22.04 43 | steps: 44 | - name: Checkout 45 | uses: actions/checkout@v4 46 | - name: Setup Go 47 | uses: actions/setup-go@v5 48 | with: 49 | go-version-file: 'go.mod' 50 | - name: run tests 51 | run: | 52 | go vet ./... 53 | go test -p 1 ./... -v 54 | go test -p 1 ./pro -v --tags ee 55 | env: 56 | DATABASE: sqlite 57 | CLIENT_MODE: "off" 58 | 59 | staticcheck: 60 | env: 61 | DATABASE: sqlite 62 | runs-on: ubuntu-22.04 63 | steps: 64 | - name: Checkout 65 | uses: actions/checkout@v4 66 | - name: Setup Go 67 | uses: actions/setup-go@v5 68 | with: 69 | go-version-file: 'go.mod' 70 | - name: run static checks 71 | run: | 72 | sudo apt update 73 | go install honnef.co/go/tools/cmd/staticcheck@latest 74 | { ~/go/bin/staticcheck -tags=ee ./... ; } 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | netmaker 2 | netmaker-arm 3 | netmaker-arm64 4 | netmaker-32 5 | netmaker-amd64 6 | cli/nmctl 7 | netclient/netclient 8 | netclient/netclient.syso 9 | netclient/build 10 | netclient/build/ 11 | !netclient/build/netclient.service 12 | netclient/files/netclient 13 | netclient/netclient-amd64 14 | netclient/netclient-arm 15 | netclient/netclient-arm64 16 | netclient/netclient-32 17 | netclient/netclient32 18 | netclient/netclient.exe 19 | config/dnsconfig/ 20 | controllers/config/dnsconfig/ 21 | controllers/data/ 22 | data/ 23 | .vscode/ 24 | .idea/ 25 | netmaker.exe 26 | netmaker.code-workspace 27 | dist/ 28 | nmctl 29 | -------------------------------------------------------------------------------- /.goreleaser.prerelease.yaml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | # You may remove this if you don't use go modules. 4 | - go mod tidy 5 | builds: 6 | - main: ./ 7 | env: 8 | - CGO_ENABLED=1 9 | ldflags: 10 | - -s -w 11 | targets: 12 | - linux_amd64 13 | binary: '{{ .ProjectName }}' 14 | 15 | - main: ./cli 16 | id: 'nmctl' 17 | env: 18 | - CGO_ENABLED=0 19 | ldflags: 20 | - -s -w 21 | targets: 22 | - linux_amd64 23 | - linux_arm64 24 | - darwin_amd64 25 | - darwin_arm64 26 | - windows_amd64 27 | binary: 'nmctl' 28 | archives: 29 | - format: binary 30 | name_template: '{{ .Binary }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}-{{ . }}{{ end }}' 31 | release: 32 | prerelease: true 33 | 34 | -------------------------------------------------------------------------------- /.goreleaser.update.yaml: -------------------------------------------------------------------------------- 1 | builds: 2 | - skip: true 3 | release: 4 | prerelease: false 5 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | # You may remove this if you don't use go modules. 4 | - go mod tidy 5 | builds: 6 | - main: ./ 7 | env: 8 | - CGO_ENABLED=1 9 | ldflags: 10 | - -s -w 11 | targets: 12 | - linux_amd64 13 | binary: '{{ .ProjectName }}' 14 | 15 | - main: ./cli 16 | id: 'nmctl' 17 | env: 18 | - CGO_ENABLED=0 19 | ldflags: 20 | - -s -w 21 | targets: 22 | - linux_amd64 23 | - linux_arm64 24 | - darwin_amd64 25 | - darwin_arm64 26 | - windows_amd64 27 | binary: 'nmctl' 28 | archives: 29 | - format: binary 30 | name_template: '{{ .Binary }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}-{{ . }}{{ end }}' 31 | release: 32 | prerelease: false 33 | 34 | -------------------------------------------------------------------------------- /.swaggo: -------------------------------------------------------------------------------- 1 | // Replace all time.Duration with int64 2 | replace time.Duration int64 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #first stage - builder 2 | FROM gravitl/go-builder:1.23.0 AS builder 3 | ARG tags 4 | WORKDIR /app 5 | COPY . . 6 | 7 | RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} . 8 | # RUN go build -tags=ee . -o netmaker main.go 9 | FROM alpine:3.21.2 10 | 11 | # add a c lib 12 | # set the working directory 13 | WORKDIR /root/ 14 | RUN apk update && apk upgrade 15 | RUN apk add --no-cache sqlite 16 | RUN mkdir -p /etc/netclient/config 17 | COPY --from=builder /app/netmaker . 18 | COPY --from=builder /app/config config 19 | EXPOSE 8081 20 | ENTRYPOINT ["./netmaker"] 21 | -------------------------------------------------------------------------------- /Dockerfile-quick: -------------------------------------------------------------------------------- 1 | #first stage - builder 2 | FROM alpine:3.21.2 3 | ARG version 4 | WORKDIR /app 5 | COPY ./netmaker /root/netmaker 6 | ENV GO111MODULE=auto 7 | 8 | # add a c lib 9 | RUN apk add gcompat iptables wireguard-tools 10 | # set the working directory 11 | WORKDIR /root/ 12 | RUN mkdir -p /etc/netclient/config 13 | EXPOSE 8081 14 | ENTRYPOINT ["./netmaker"] 15 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | Netmaker is reliant on secure networking. If you find a vulnerability or bug please report it. 4 | Depending on complexity or severity, the Gravitl team may compensate (aka. bug bounty) the reporter. 5 | However, there is no official bug bounty program up yet for the Netmaker project. 6 | 7 | ## Supported Versions 8 | - We currently are only able to support work on the latest version(s) 9 | 10 | ## Reporting a Vulnerability 11 | 12 | Please report security issues to `info@netmaker.io` 13 | -------------------------------------------------------------------------------- /auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/logic" 5 | "github.com/gravitl/netmaker/models" 6 | "golang.org/x/oauth2" 7 | ) 8 | 9 | // == consts == 10 | const ( 11 | node_signin_length = 64 12 | ) 13 | 14 | var ( 15 | auth_provider *oauth2.Config 16 | ) 17 | 18 | func isUserIsAllowed(username, network string) (*models.User, error) { 19 | 20 | user, err := logic.GetUser(username) 21 | if err != nil { // user must not exist, so try to make one 22 | return &models.User{}, err 23 | } 24 | 25 | return user, nil 26 | } 27 | -------------------------------------------------------------------------------- /cli/cmd/acl/allow.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/gravitl/netmaker/cli/functions" 8 | "github.com/gravitl/netmaker/logic/acls" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var aclAllowCmd = &cobra.Command{ 13 | Use: "allow [NETWORK NAME] [NODE_1_ID] [NODE_2_ID]", 14 | Args: cobra.ExactArgs(3), 15 | Short: "Allow access from one node to another", 16 | Long: `Allow access from one node to another`, 17 | Run: func(cmd *cobra.Command, args []string) { 18 | network := args[0] 19 | fromNodeID := args[1] 20 | toNodeID := args[2] 21 | 22 | if fromNodeID == toNodeID { 23 | log.Fatal("Cannot allow access from a node to itself") 24 | } 25 | 26 | // get current acls 27 | res := functions.GetACL(network) 28 | if res == nil { 29 | log.Fatalf("Could not load network ACLs") 30 | } 31 | 32 | payload := *res 33 | 34 | if _, ok := payload[acls.AclID(fromNodeID)]; !ok { 35 | log.Fatalf("Node %s does not exist", fromNodeID) 36 | } 37 | if _, ok := payload[acls.AclID(toNodeID)]; !ok { 38 | log.Fatalf("Node %s does not exist", toNodeID) 39 | } 40 | 41 | // update acls 42 | payload[acls.AclID(fromNodeID)][acls.AclID(toNodeID)] = acls.Allowed 43 | payload[acls.AclID(toNodeID)][acls.AclID(fromNodeID)] = acls.Allowed 44 | 45 | functions.UpdateACL(network, &payload) 46 | fmt.Println("Success") 47 | }, 48 | } 49 | 50 | func init() { 51 | rootCmd.AddCommand(aclAllowCmd) 52 | } 53 | -------------------------------------------------------------------------------- /cli/cmd/acl/deny.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/gravitl/netmaker/cli/functions" 8 | "github.com/gravitl/netmaker/logic/acls" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var aclDenyCmd = &cobra.Command{ 13 | Use: "deny [NETWORK NAME] [NODE_1_ID] [NODE_2_ID]", 14 | Args: cobra.ExactArgs(3), 15 | Short: "Deny access from one node to another", 16 | Long: `Deny access from one node to another`, 17 | Run: func(cmd *cobra.Command, args []string) { 18 | network := args[0] 19 | fromNodeID := args[1] 20 | toNodeID := args[2] 21 | 22 | if fromNodeID == toNodeID { 23 | log.Fatal("Cannot deny access to self") 24 | } 25 | 26 | // get current acls 27 | res := functions.GetACL(network) 28 | if res == nil { 29 | log.Fatalf("Could not load network ACLs") 30 | } 31 | 32 | payload := *res 33 | 34 | if _, ok := payload[acls.AclID(fromNodeID)]; !ok { 35 | log.Fatalf("Node [%s] does not exist", fromNodeID) 36 | } 37 | if _, ok := payload[acls.AclID(toNodeID)]; !ok { 38 | log.Fatalf("Node [%s] does not exist", toNodeID) 39 | } 40 | 41 | // update acls 42 | payload[acls.AclID(fromNodeID)][acls.AclID(toNodeID)] = acls.NotAllowed 43 | payload[acls.AclID(toNodeID)][acls.AclID(fromNodeID)] = acls.NotAllowed 44 | 45 | functions.UpdateACL(network, &payload) 46 | fmt.Println("Success") 47 | }, 48 | } 49 | 50 | func init() { 51 | rootCmd.AddCommand(aclDenyCmd) 52 | } 53 | -------------------------------------------------------------------------------- /cli/cmd/acl/list.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/gravitl/netmaker/cli/cmd/commons" 7 | "github.com/gravitl/netmaker/cli/functions" 8 | "github.com/gravitl/netmaker/logic/acls" 9 | "github.com/guumaster/tablewriter" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var aclListCmd = &cobra.Command{ 14 | Use: "list [NETWORK NAME]", 15 | Args: cobra.ExactArgs(1), 16 | Short: "List all ACLs associated with a network", 17 | Long: `List all ACLs associated with a network`, 18 | Run: func(cmd *cobra.Command, args []string) { 19 | aclSource := (map[acls.AclID]acls.ACL)(*functions.GetACL(args[0])) 20 | switch commons.OutputFormat { 21 | case commons.JsonOutput: 22 | functions.PrettyPrint(aclSource) 23 | default: 24 | table := tablewriter.NewWriter(os.Stdout) 25 | table.SetHeader([]string{"From", "To", "Status"}) 26 | for id, acl := range aclSource { 27 | for k, v := range (map[acls.AclID]byte)(acl) { 28 | row := []string{string(id), string(k)} 29 | switch v { 30 | case acls.NotAllowed: 31 | row = append(row, "Not Allowed") 32 | case acls.NotPresent: 33 | row = append(row, "Not Present") 34 | case acls.Allowed: 35 | row = append(row, "Allowed") 36 | } 37 | table.Append(row) 38 | } 39 | } 40 | table.Render() 41 | } 42 | }, 43 | } 44 | 45 | func init() { 46 | rootCmd.AddCommand(aclListCmd) 47 | } 48 | -------------------------------------------------------------------------------- /cli/cmd/acl/root.go: -------------------------------------------------------------------------------- 1 | package acl 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "acl", 12 | Short: "Manage Access Control Lists (ACLs)", 13 | Long: `Manage Access Control Lists (ACLs)`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/commons/globals.go: -------------------------------------------------------------------------------- 1 | package commons 2 | 3 | // OutputFormat flag defines the output format to stdout (Enum:- json) 4 | var OutputFormat string 5 | 6 | const ( 7 | // JsonOutput refers to json format output to stdout 8 | JsonOutput = "json" 9 | ) 10 | -------------------------------------------------------------------------------- /cli/cmd/context/delete.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var contextDeleteCmd = &cobra.Command{ 9 | Use: "delete [NAME]", 10 | Args: cobra.ExactArgs(1), 11 | Short: "Delete a context", 12 | Long: `Delete a context`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | config.DeleteContext(args[0]) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(contextDeleteCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/context/list.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var contextListCmd = &cobra.Command{ 9 | Use: "list", 10 | Args: cobra.NoArgs, 11 | Short: "List all contexts", 12 | Long: `List all contexts`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | config.ListAll() 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(contextListCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/context/root.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "context", 12 | Short: "Manage various netmaker server configurations", 13 | Long: `Manage various netmaker server configurations`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/context/set.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/gravitl/netmaker/cli/config" 8 | "github.com/gravitl/netmaker/cli/functions" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var ( 13 | endpoint string 14 | username string 15 | password string 16 | masterKey string 17 | sso bool 18 | tenantId string 19 | saas bool 20 | ) 21 | 22 | var contextSetCmd = &cobra.Command{ 23 | Use: "set [NAME]", 24 | Args: cobra.ExactArgs(1), 25 | Short: "Create a context or update an existing one", 26 | Long: `Create a context or update an existing one`, 27 | Run: func(cmd *cobra.Command, args []string) { 28 | ctx := config.Context{ 29 | Endpoint: endpoint, 30 | Username: username, 31 | Password: password, 32 | MasterKey: masterKey, 33 | SSO: sso, 34 | TenantId: tenantId, 35 | Saas: saas, 36 | } 37 | if !ctx.Saas { 38 | if ctx.Username == "" && ctx.MasterKey == "" && !ctx.SSO { 39 | log.Fatal("Either username/password or master key is required") 40 | cmd.Usage() 41 | } 42 | if ctx.Endpoint == "" { 43 | log.Fatal("Endpoint is required when for self-hosted tenants") 44 | cmd.Usage() 45 | } 46 | } else { 47 | if ctx.TenantId == "" { 48 | log.Fatal("Tenant ID is required for SaaS tenants") 49 | cmd.Usage() 50 | } 51 | ctx.Endpoint = fmt.Sprintf(functions.TenantUrlTemplate, tenantId) 52 | if ctx.Username == "" && ctx.Password == "" && !ctx.SSO { 53 | log.Fatal("Username/password is required for non-SSO SaaS contexts") 54 | cmd.Usage() 55 | } 56 | } 57 | config.SetContext(args[0], ctx) 58 | }, 59 | } 60 | 61 | func init() { 62 | contextSetCmd.Flags().StringVar(&endpoint, "endpoint", "", "Endpoint of the API Server") 63 | contextSetCmd.Flags().StringVar(&username, "username", "", "Username") 64 | contextSetCmd.Flags().StringVar(&password, "password", "", "Password") 65 | contextSetCmd.MarkFlagsRequiredTogether("username", "password") 66 | contextSetCmd.Flags().BoolVar(&sso, "sso", false, "Login via Single Sign On (SSO)?") 67 | contextSetCmd.Flags().StringVar(&masterKey, "master_key", "", "Master Key") 68 | contextSetCmd.Flags().StringVar(&tenantId, "tenant_id", "", "Tenant ID") 69 | contextSetCmd.Flags().BoolVar(&saas, "saas", false, "Is this context for a SaaS tenant?") 70 | rootCmd.AddCommand(contextSetCmd) 71 | } 72 | -------------------------------------------------------------------------------- /cli/cmd/context/use.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var contextUseCmd = &cobra.Command{ 9 | Use: "use [NAME]", 10 | Args: cobra.ExactArgs(1), 11 | Short: "Set the current context", 12 | Long: `Set the current context`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | config.SetCurrentContext(args[0]) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(contextUseCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/dns/create.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/gravitl/netmaker/models" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var dnsCreateCmd = &cobra.Command{ 12 | Use: "create", 13 | Args: cobra.NoArgs, 14 | Short: "Create a DNS entry", 15 | Long: `Create a DNS entry`, 16 | Run: func(cmd *cobra.Command, args []string) { 17 | if address == "" && address6 == "" { 18 | log.Fatal("Either IPv4 or IPv6 address is required") 19 | } 20 | dnsEntry := &models.DNSEntry{Name: dnsName, Address: address, Address6: address6, Network: networkName} 21 | functions.PrettyPrint(functions.CreateDNS(networkName, dnsEntry)) 22 | }, 23 | } 24 | 25 | func init() { 26 | dnsCreateCmd.Flags().StringVar(&dnsName, "name", "", "Name of the DNS entry") 27 | dnsCreateCmd.MarkFlagRequired("name") 28 | dnsCreateCmd.Flags().StringVar(&networkName, "network", "", "Name of the Network") 29 | dnsCreateCmd.MarkFlagRequired("network") 30 | dnsCreateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 Address") 31 | dnsCreateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 Address") 32 | rootCmd.AddCommand(dnsCreateCmd) 33 | } 34 | -------------------------------------------------------------------------------- /cli/cmd/dns/delete.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var dnsDeleteCmd = &cobra.Command{ 9 | Use: "delete [NETWORK NAME] [DOMAIN NAME]", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Delete a DNS entry", 12 | Long: `Delete a DNS entry`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.DeleteDNS(args[0], args[1])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(dnsDeleteCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/dns/flags.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | var ( 4 | dnsName string 5 | address string 6 | address6 string 7 | networkName string 8 | dnsType string 9 | ) 10 | -------------------------------------------------------------------------------- /cli/cmd/dns/list.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/gravitl/netmaker/cli/cmd/commons" 8 | "github.com/gravitl/netmaker/cli/functions" 9 | "github.com/gravitl/netmaker/models" 10 | "github.com/guumaster/tablewriter" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var dnsListCmd = &cobra.Command{ 15 | Use: "list", 16 | Args: cobra.NoArgs, 17 | Short: "List DNS entries", 18 | Long: `List DNS entries`, 19 | Run: func(cmd *cobra.Command, args []string) { 20 | var data []models.DNSEntry 21 | if networkName != "" { 22 | switch dnsType { 23 | case "node": 24 | data = *functions.GetNodeDNS(networkName) 25 | case "custom": 26 | data = *functions.GetCustomDNS(networkName) 27 | case "network", "": 28 | data = *functions.GetNetworkDNS(networkName) 29 | default: 30 | fmt.Println("Invalid DNS type provided ", dnsType) 31 | } 32 | } else { 33 | data = *functions.GetDNS() 34 | } 35 | switch commons.OutputFormat { 36 | case commons.JsonOutput: 37 | functions.PrettyPrint(data) 38 | default: 39 | table := tablewriter.NewWriter(os.Stdout) 40 | table.SetHeader([]string{"Name", "Network", "IPv4 Address", "IPv6 Address"}) 41 | for _, d := range data { 42 | table.Append([]string{d.Name, d.Network, d.Address, d.Address6}) 43 | } 44 | table.Render() 45 | } 46 | }, 47 | } 48 | 49 | func init() { 50 | dnsListCmd.Flags().StringVar(&networkName, "network", "", "Network name") 51 | dnsListCmd.Flags().StringVar(&dnsType, "type", "", "Type of DNS records to fetch ENUM(node, custom, network)") 52 | rootCmd.AddCommand(dnsListCmd) 53 | } 54 | -------------------------------------------------------------------------------- /cli/cmd/dns/push.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var dnsPushCmd = &cobra.Command{ 11 | Use: "push", 12 | Args: cobra.NoArgs, 13 | Short: "Push latest DNS entries", 14 | Long: `Push latest DNS entries`, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | fmt.Println(*functions.PushDNS()) 17 | }, 18 | } 19 | 20 | func init() { 21 | rootCmd.AddCommand(dnsPushCmd) 22 | } 23 | -------------------------------------------------------------------------------- /cli/cmd/dns/root.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "dns", 12 | Short: "Manage DNS entries associated with a network", 13 | Long: `Manage DNS entries associated with a network`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/enrollment_key/create.go: -------------------------------------------------------------------------------- 1 | package enrollment_key 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/gravitl/netmaker/models" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var ( 12 | expiration int 13 | usesRemaining int 14 | networks string 15 | unlimited bool 16 | tags string 17 | ) 18 | 19 | var enrollmentKeyCreateCmd = &cobra.Command{ 20 | Use: "create", 21 | Args: cobra.NoArgs, 22 | Short: "Create an enrollment key", 23 | Long: `Create an enrollment key`, 24 | Run: func(cmd *cobra.Command, args []string) { 25 | enrollKey := &models.APIEnrollmentKey{ 26 | Expiration: int64(expiration), 27 | UsesRemaining: usesRemaining, 28 | Unlimited: unlimited, 29 | } 30 | if networks != "" { 31 | enrollKey.Networks = strings.Split(networks, ",") 32 | } 33 | if tags != "" { 34 | enrollKey.Tags = strings.Split(tags, ",") 35 | } 36 | functions.PrettyPrint(functions.CreateEnrollmentKey(enrollKey)) 37 | }, 38 | } 39 | 40 | func init() { 41 | enrollmentKeyCreateCmd.Flags().IntVar(&expiration, "expiration", 0, "Expiration time of the key in UNIX timestamp format") 42 | enrollmentKeyCreateCmd.Flags().IntVar(&usesRemaining, "uses", 0, "Number of times this key can be used") 43 | enrollmentKeyCreateCmd.Flags().StringVar(&networks, "networks", "", "Comma-separated list of networks which the enrollment key can access") 44 | enrollmentKeyCreateCmd.Flags().BoolVar(&unlimited, "unlimited", false, "Should the key have unlimited uses ?") 45 | enrollmentKeyCreateCmd.Flags().StringVar(&tags, "tags", "", "Comma-separated list of any additional tags") 46 | rootCmd.AddCommand(enrollmentKeyCreateCmd) 47 | } 48 | -------------------------------------------------------------------------------- /cli/cmd/enrollment_key/delete.go: -------------------------------------------------------------------------------- 1 | package enrollment_key 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var enrollmentKeyDeleteCmd = &cobra.Command{ 11 | Use: "delete keyID", 12 | Args: cobra.ExactArgs(1), 13 | Short: "Delete an enrollment key", 14 | Long: `Delete an enrollment key`, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | functions.DeleteEnrollmentKey(args[0]) 17 | fmt.Println("Enrollment key ", args[0], " deleted") 18 | }, 19 | } 20 | 21 | func init() { 22 | rootCmd.AddCommand(enrollmentKeyDeleteCmd) 23 | } 24 | -------------------------------------------------------------------------------- /cli/cmd/enrollment_key/list.go: -------------------------------------------------------------------------------- 1 | package enrollment_key 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var enrollmentKeyListCmd = &cobra.Command{ 9 | Use: "list", 10 | Args: cobra.NoArgs, 11 | Short: "List enrollment keys", 12 | Long: `List enrollment keys`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetEnrollmentKeys()) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(enrollmentKeyListCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/enrollment_key/root.go: -------------------------------------------------------------------------------- 1 | package enrollment_key 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "enrollment_key", 12 | Short: "Manage Enrollment Keys", 13 | Long: `Manage Enrollment Keys`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/ext_client/config.go: -------------------------------------------------------------------------------- 1 | package ext_client 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var extClientConfigCmd = &cobra.Command{ 11 | Use: "config [NETWORK NAME] [EXTERNAL CLIENT ID]", 12 | Args: cobra.ExactArgs(2), 13 | Short: "Get an External Client Configuration", 14 | Long: `Get an External Client Configuration`, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | fmt.Println(functions.GetExtClientConfig(args[0], args[1])) 17 | }, 18 | } 19 | 20 | var extClientHAConfigCmd = &cobra.Command{ 21 | Use: "auto_config [NETWORK NAME]", 22 | Args: cobra.ExactArgs(1), 23 | Short: "Get an External Client Configuration", 24 | Long: `Get an External Client Configuration`, 25 | Run: func(cmd *cobra.Command, args []string) { 26 | fmt.Println(functions.GetExtClientHAConfig(args[0])) 27 | }, 28 | } 29 | 30 | func init() { 31 | rootCmd.AddCommand(extClientConfigCmd) 32 | rootCmd.AddCommand(extClientHAConfigCmd) 33 | } 34 | -------------------------------------------------------------------------------- /cli/cmd/ext_client/create.go: -------------------------------------------------------------------------------- 1 | package ext_client 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/gravitl/netmaker/models" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var ( 12 | extClientID string 13 | publicKey string 14 | dns string 15 | allowedips []string 16 | ) 17 | 18 | var extClientCreateCmd = &cobra.Command{ 19 | Use: "create [NETWORK NAME] [NODE ID]", 20 | Args: cobra.ExactArgs(2), 21 | Short: "Create an External Client", 22 | Long: `Create an External Client`, 23 | Run: func(cmd *cobra.Command, args []string) { 24 | extClient := models.CustomExtClient{ 25 | ClientID: extClientID, 26 | PublicKey: publicKey, 27 | DNS: dns, 28 | ExtraAllowedIPs: allowedips, 29 | } 30 | 31 | functions.CreateExtClient(args[0], args[1], extClient) 32 | fmt.Println("Success") 33 | }, 34 | } 35 | 36 | func init() { 37 | extClientCreateCmd.Flags().StringVar(&extClientID, "id", "", "ID of the external client") 38 | extClientCreateCmd.Flags().StringVar(&publicKey, "public_key", "", "updated public key of the external client") 39 | extClientCreateCmd.Flags().StringVar(&dns, "dns", "", "updated DNS of the external client") 40 | extClientCreateCmd.Flags().StringSliceVar(&allowedips, "allowedips", []string{}, "updated extra allowed IPs of the external client") 41 | rootCmd.AddCommand(extClientCreateCmd) 42 | } 43 | -------------------------------------------------------------------------------- /cli/cmd/ext_client/delete.go: -------------------------------------------------------------------------------- 1 | package ext_client 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var extClientDeleteCmd = &cobra.Command{ 9 | Use: "delete [NETWORK NAME] [EXTERNAL CLIENT ID]", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Delete an External Client", 12 | Long: `Delete an External Client`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.DeleteExtClient(args[0], args[1])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(extClientDeleteCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/ext_client/get.go: -------------------------------------------------------------------------------- 1 | package ext_client 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var extClientGetCmd = &cobra.Command{ 9 | Use: "get [NETWORK NAME] [EXTERNAL CLIENT ID]", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Get an External Client", 12 | Long: `Get an External Client`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetExtClient(args[0], args[1])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(extClientGetCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/ext_client/list.go: -------------------------------------------------------------------------------- 1 | package ext_client 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "time" 7 | 8 | "github.com/gravitl/netmaker/cli/cmd/commons" 9 | "github.com/gravitl/netmaker/cli/functions" 10 | "github.com/gravitl/netmaker/models" 11 | "github.com/guumaster/tablewriter" 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | var networkName string 16 | 17 | var extClientListCmd = &cobra.Command{ 18 | Use: "list", 19 | Args: cobra.NoArgs, 20 | Short: "List External Clients", 21 | Long: `List External Clients`, 22 | Run: func(cmd *cobra.Command, args []string) { 23 | var data []models.ExtClient 24 | if networkName != "" { 25 | data = *functions.GetNetworkExtClients(networkName) 26 | } else { 27 | data = *functions.GetAllExtClients() 28 | } 29 | switch commons.OutputFormat { 30 | case commons.JsonOutput: 31 | functions.PrettyPrint(data) 32 | default: 33 | table := tablewriter.NewWriter(os.Stdout) 34 | table.SetHeader([]string{"Client ID", "Network", "IPv4 Address", "IPv6 Address", "Enabled", "Last Modified"}) 35 | for _, d := range data { 36 | table.Append([]string{d.ClientID, d.Network, d.Address, d.Address6, strconv.FormatBool(d.Enabled), time.Unix(d.LastModified, 0).String()}) 37 | } 38 | table.Render() 39 | } 40 | }, 41 | } 42 | 43 | func init() { 44 | extClientListCmd.Flags().StringVar(&networkName, "network", "", "Network name") 45 | rootCmd.AddCommand(extClientListCmd) 46 | } 47 | -------------------------------------------------------------------------------- /cli/cmd/ext_client/root.go: -------------------------------------------------------------------------------- 1 | package ext_client 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "ext_client", 12 | Short: "Manage External Clients", 13 | Long: `Manage External Clients`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/ext_client/update.go: -------------------------------------------------------------------------------- 1 | package ext_client 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "os" 7 | 8 | "github.com/gravitl/netmaker/cli/functions" 9 | "github.com/gravitl/netmaker/models" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var ( 14 | extClientUpdateFile string 15 | ) 16 | 17 | var extClientUpdateCmd = &cobra.Command{ 18 | Use: "update [NETWORK NAME] [EXTERNAL CLIENT ID]", 19 | Args: cobra.ExactArgs(2), 20 | Short: "Update an External Client", 21 | Long: `Update an External Client`, 22 | Run: func(cmd *cobra.Command, args []string) { 23 | var ( 24 | network = args[0] 25 | clientID = args[1] 26 | extClient = &models.CustomExtClient{} 27 | ) 28 | if extClientUpdateFile != "" { 29 | content, err := os.ReadFile(extClientUpdateFile) 30 | if err != nil { 31 | log.Fatal("Error when opening file: ", err) 32 | } 33 | if err := json.Unmarshal(content, extClient); err != nil { 34 | log.Fatal(err) 35 | } 36 | } else { 37 | extClient.ClientID = extClientID 38 | extClient.PublicKey = publicKey 39 | extClient.DNS = dns 40 | } 41 | functions.PrettyPrint(functions.UpdateExtClient(network, clientID, extClient)) 42 | }, 43 | } 44 | 45 | func init() { 46 | extClientUpdateCmd.Flags().StringVar(&extClientID, "id", "", "updated ID of the external client") 47 | extClientUpdateCmd.Flags().StringVar(&extClientUpdateFile, "file", "", "Filepath of updated external client definition in JSON") 48 | extClientUpdateCmd.Flags().StringVar(&publicKey, "public_key", "", "updated public key of the external client") 49 | extClientUpdateCmd.Flags().StringVar(&dns, "dns", "", "updated DNS of the external client") 50 | extClientUpdateCmd.Flags().StringSliceVar(&allowedips, "allowedips", []string{}, "updated extra allowed IPs of the external client") 51 | rootCmd.AddCommand(extClientUpdateCmd) 52 | } 53 | -------------------------------------------------------------------------------- /cli/cmd/failover/disable.go: -------------------------------------------------------------------------------- 1 | package failover 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var disableFailoverCmd = &cobra.Command{ 9 | Use: "disable [NODE ID]", 10 | Args: cobra.ExactArgs(1), 11 | Short: "Disable failover for a given Node", 12 | Long: `Disable failover for a given Node`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.DisableNodeFailover(args[0])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(disableFailoverCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/failover/enable.go: -------------------------------------------------------------------------------- 1 | package failover 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var enableFailoverCmd = &cobra.Command{ 9 | Use: "enable [NODE ID]", 10 | Args: cobra.ExactArgs(1), 11 | Short: "Enable failover for a given Node", 12 | Long: `Enable failover for a given Node`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.EnableNodeFailover(args[0])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(enableFailoverCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/failover/root.go: -------------------------------------------------------------------------------- 1 | package failover 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "failover", 12 | Short: "Enable/Disable failover for a node associated with a network", 13 | Long: `Enable/Disable failover for a node associated with a network`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/gateway/create.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/gravitl/netmaker/models" 6 | "github.com/spf13/cobra" 7 | "strings" 8 | ) 9 | 10 | var externalClientDNS string 11 | var isInternetGateway bool 12 | var metadata string 13 | var persistentKeepAlive uint 14 | var mtu uint 15 | 16 | var gatewayCreateCmd = &cobra.Command{ 17 | Use: "create [NETWORK NAME] [NODE ID] [RELAYED NODES ID (comma separated)]", 18 | Args: cobra.ExactArgs(3), 19 | Short: "Create a new Gateway on a Netmaker network.", 20 | Long: ` 21 | Configures a node as a gateway in a specified network, allowing it to relay traffic for other nodes. The gateway can also function as an internet gateway if specified. 22 | 23 | Arguments: 24 | NETWORK NAME: The name of the network where the gateway will be created. 25 | NODE ID: The ID of the node to be configured as a gateway. 26 | RELAYED NODES ID: A comma-separated list of node IDs that will be relayed through this gateway. 27 | `, 28 | Run: func(cmd *cobra.Command, args []string) { 29 | functions.PrettyPrint( 30 | functions.CreateGateway( 31 | models.IngressRequest{ 32 | ExtclientDNS: externalClientDNS, 33 | IsInternetGateway: isInternetGateway, 34 | Metadata: metadata, 35 | PersistentKeepalive: int32(persistentKeepAlive), 36 | MTU: int32(mtu), 37 | }, 38 | models.RelayRequest{ 39 | NodeID: args[0], 40 | NetID: args[1], 41 | RelayedNodes: strings.Split(args[2], ","), 42 | }, 43 | ), 44 | ) 45 | }, 46 | } 47 | 48 | func init() { 49 | gatewayCreateCmd.Flags().StringVarP(&externalClientDNS, "dns", "d", "", "the IP address of the DNS server to be used by external clients") 50 | gatewayCreateCmd.Flags().BoolVarP(&isInternetGateway, "internet", "i", false, "if set, the gateway will route traffic to the internet") 51 | gatewayCreateCmd.Flags().StringVarP(&metadata, "note", "n", "", "description or metadata to be associated with the gateway") 52 | gatewayCreateCmd.Flags().UintVarP(&persistentKeepAlive, "keep-alive", "k", 20, "the keep-alive interval (in seconds) for maintaining persistent connections") 53 | gatewayCreateCmd.Flags().UintVarP(&mtu, "mtu", "m", 1420, "the maximum transmission unit (MTU) size in bytes") 54 | rootCmd.AddCommand(gatewayCreateCmd) 55 | } 56 | -------------------------------------------------------------------------------- /cli/cmd/gateway/delete.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var gatewayDeleteCmd = &cobra.Command{ 9 | Use: "delete [NETWORK NAME] [NODE ID]", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Delete a Gateway.", 12 | Long: ` 13 | Removes the gateway configuration from a node in a specified network. The node itself remains, but it will no longer function as a gateway. 14 | 15 | Arguments: 16 | NETWORK NAME: The name of the network from which the gateway configuration should be removed. 17 | NODE ID: The ID of the node that is currently acting as a gateway. 18 | `, 19 | Aliases: []string{"rm"}, 20 | Run: func(cmd *cobra.Command, args []string) { 21 | functions.PrettyPrint(functions.DeleteGateway(args[0], args[1])) 22 | }, 23 | } 24 | 25 | func init() { 26 | rootCmd.AddCommand(gatewayDeleteCmd) 27 | } 28 | -------------------------------------------------------------------------------- /cli/cmd/gateway/root.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // rootCmd represents the base command when called without any subcommands. 8 | var rootCmd = &cobra.Command{ 9 | Use: "gateway", 10 | Short: "Manage Gateways.", 11 | Long: `Manage Gateways.`, 12 | Aliases: []string{"gw"}, 13 | } 14 | 15 | // GetRoot returns the root subcommand. 16 | func GetRoot() *cobra.Command { 17 | return rootCmd 18 | } 19 | -------------------------------------------------------------------------------- /cli/cmd/host/add_network.go: -------------------------------------------------------------------------------- 1 | package host 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var addHostNetworkCmd = &cobra.Command{ 9 | Use: "add_network HostID Network", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Add a network to a host", 12 | Long: `Add a network to a host`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.AddHostToNetwork(args[0], args[1])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(addHostNetworkCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/host/delete.go: -------------------------------------------------------------------------------- 1 | package host 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var force bool 9 | 10 | var hostDeleteCmd = &cobra.Command{ 11 | Use: "delete HostID", 12 | Args: cobra.ExactArgs(1), 13 | Short: "Delete a host", 14 | Long: `Delete a host`, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | functions.PrettyPrint(functions.DeleteHost(args[0], force)) 17 | }, 18 | } 19 | 20 | func init() { 21 | rootCmd.AddCommand(hostDeleteCmd) 22 | hostDeleteCmd.PersistentFlags().BoolVarP(&force, "force", "f", false, "delete even if part of network(s)") 23 | } 24 | -------------------------------------------------------------------------------- /cli/cmd/host/delete_network.go: -------------------------------------------------------------------------------- 1 | package host 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var deleteHostNetworkCmd = &cobra.Command{ 9 | Use: "delete_network HostID Network", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Delete a network from a host", 12 | Long: `Delete a network from a host`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.DeleteHostFromNetwork(args[0], args[1])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(deleteHostNetworkCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/host/list.go: -------------------------------------------------------------------------------- 1 | package host 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var hostListCmd = &cobra.Command{ 9 | Use: "list", 10 | Args: cobra.NoArgs, 11 | Short: "List all hosts", 12 | Long: `List all hosts`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetHosts()) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(hostListCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/host/refresh_keys.go: -------------------------------------------------------------------------------- 1 | package host 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var hostRefreshKeysCmd = &cobra.Command{ 9 | Use: "refresh_keys [HOST ID] ", 10 | Args: cobra.MaximumNArgs(1), 11 | Short: "Refresh wireguard keys on host", 12 | Long: `Refresh wireguard keys on specified or all hosts 13 | If HOSTID is not specified, all hosts will be updated`, 14 | Run: func(cmd *cobra.Command, args []string) { 15 | functions.PrettyPrint(functions.RefreshKeys(args[0])) 16 | }, 17 | } 18 | 19 | func init() { 20 | rootCmd.AddCommand(hostRefreshKeysCmd) 21 | } 22 | -------------------------------------------------------------------------------- /cli/cmd/host/root.go: -------------------------------------------------------------------------------- 1 | package host 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "host", 12 | Short: "Manage hosts", 13 | Long: `Manage hosts`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/host/update.go: -------------------------------------------------------------------------------- 1 | package host 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "os" 7 | 8 | "github.com/spf13/cobra" 9 | 10 | "github.com/gravitl/netmaker/cli/functions" 11 | "github.com/gravitl/netmaker/models" 12 | ) 13 | 14 | var ( 15 | apiHostFilePath string 16 | endpoint string 17 | endpoint6 string 18 | name string 19 | listenPort int 20 | mtu int 21 | isStaticPort bool 22 | isStatic bool 23 | isDefault bool 24 | keepAlive int 25 | ) 26 | 27 | var hostUpdateCmd = &cobra.Command{ 28 | Use: "update HostID", 29 | Args: cobra.ExactArgs(1), 30 | Short: "Update a host", 31 | Long: `Update a host`, 32 | Run: func(cmd *cobra.Command, args []string) { 33 | apiHost := &models.ApiHost{} 34 | if apiHostFilePath != "" { 35 | content, err := os.ReadFile(apiHostFilePath) 36 | if err != nil { 37 | log.Fatal("Error when opening file: ", err) 38 | } 39 | if err := json.Unmarshal(content, apiHost); err != nil { 40 | log.Fatal(err) 41 | } 42 | } else { 43 | apiHost.ID = args[0] 44 | apiHost.EndpointIP = endpoint 45 | apiHost.EndpointIPv6 = endpoint6 46 | apiHost.Name = name 47 | apiHost.ListenPort = listenPort 48 | apiHost.MTU = mtu 49 | apiHost.IsStaticPort = isStaticPort 50 | apiHost.IsStatic = isStatic 51 | apiHost.IsDefault = isDefault 52 | apiHost.PersistentKeepalive = keepAlive 53 | } 54 | functions.PrettyPrint(functions.UpdateHost(args[0], apiHost)) 55 | }, 56 | } 57 | 58 | func init() { 59 | hostUpdateCmd.Flags().StringVar(&apiHostFilePath, "file", "", "Path to host_definition.json") 60 | hostUpdateCmd.Flags().StringVar(&endpoint, "endpoint", "", "Endpoint of the Host") 61 | hostUpdateCmd.Flags().StringVar(&endpoint6, "endpoint6", "", "IPv6 Endpoint of the Host") 62 | hostUpdateCmd.Flags().StringVar(&name, "name", "", "Host name") 63 | hostUpdateCmd.Flags().IntVar(&listenPort, "listen_port", 0, "Listen port of the host") 64 | hostUpdateCmd.Flags().IntVar(&mtu, "mtu", 0, "Host MTU size") 65 | hostUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval (seconds) in which packets are sent to keep connections open with peers") 66 | hostUpdateCmd.Flags().BoolVar(&isStaticPort, "static_port", false, "Make Host Static Port?") 67 | hostUpdateCmd.Flags().BoolVar(&isStatic, "static_endpoint", false, "Make Host Static Endpoint?") 68 | hostUpdateCmd.Flags().BoolVar(&isDefault, "default", false, "Make Host Default ?") 69 | rootCmd.AddCommand(hostUpdateCmd) 70 | } 71 | -------------------------------------------------------------------------------- /cli/cmd/logs.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var getLogsCmd = &cobra.Command{ 11 | Use: "logs", 12 | Args: cobra.NoArgs, 13 | Short: "Retrieve server logs", 14 | Long: `Retrieve server logs`, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | fmt.Println(functions.GetLogs()) 17 | }, 18 | } 19 | 20 | func init() { 21 | rootCmd.AddCommand(getLogsCmd) 22 | } 23 | -------------------------------------------------------------------------------- /cli/cmd/metrics/all.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var metricsAllCmd = &cobra.Command{ 9 | Use: "all", 10 | Args: cobra.NoArgs, 11 | Short: "Retrieve all metrics", 12 | Long: `Retrieve all metrics`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetAllMetrics()) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(metricsAllCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/metrics/network.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var metricsNetworkCmd = &cobra.Command{ 9 | Use: "network [NETWORK NAME]", 10 | Args: cobra.ExactArgs(1), 11 | Short: "Retrieve network metrics", 12 | Long: `Retrieve network metrics`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetNetworkNodeMetrics(args[0])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(metricsNetworkCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/metrics/network_ext.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var metricsNetworkExtCmd = &cobra.Command{ 9 | Use: "network_ext [NETWORK NAME]", 10 | Args: cobra.ExactArgs(1), 11 | Short: "Retrieve metrics of external clients on a given network", 12 | Long: `Retrieve metrics of external clients on a given network`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetNetworkExtMetrics(args[0])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(metricsNetworkExtCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/metrics/node.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var metricsNodeCmd = &cobra.Command{ 9 | Use: "node [NETWORK NAME] [NODE ID]", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Retrieve node metrics", 12 | Long: `Retrieve node metrics`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetNodeMetrics(args[0], args[1])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(metricsNodeCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/metrics/root.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "metrics", 12 | Short: "Fetch metrics of nodes/networks", 13 | Long: `Fetch metrics of nodes/networks`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/network/delete.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var networkDeleteCmd = &cobra.Command{ 11 | Use: "delete [NAME]", 12 | Short: "Delete a Network", 13 | Long: `Delete a Network`, 14 | Args: cobra.ExactArgs(1), 15 | Run: func(cmd *cobra.Command, args []string) { 16 | fmt.Println(*functions.DeleteNetwork(args[0])) 17 | }, 18 | } 19 | 20 | func init() { 21 | rootCmd.AddCommand(networkDeleteCmd) 22 | } 23 | -------------------------------------------------------------------------------- /cli/cmd/network/flags.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | var ( 4 | networkDefinitionFilePath string 5 | netID string 6 | address string 7 | address6 string 8 | udpHolePunch bool 9 | defaultACL bool 10 | defaultInterface string 11 | defaultListenPort int 12 | nodeLimit int 13 | defaultKeepalive int 14 | allowManualSignUp bool 15 | defaultMTU int 16 | ) 17 | -------------------------------------------------------------------------------- /cli/cmd/network/get.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var networkGetCmd = &cobra.Command{ 9 | Use: "get [NETWORK NAME]", 10 | Short: "Get a Network", 11 | Long: `Get a Network`, 12 | Args: cobra.ExactArgs(1), 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetNetwork(args[0])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(networkGetCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/network/list.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "os" 5 | "time" 6 | 7 | "github.com/gravitl/netmaker/cli/cmd/commons" 8 | "github.com/gravitl/netmaker/cli/functions" 9 | "github.com/olekukonko/tablewriter" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var networkListCmd = &cobra.Command{ 14 | Use: "list", 15 | Short: "List all Networks", 16 | Long: `List all Networks`, 17 | Args: cobra.NoArgs, 18 | Run: func(cmd *cobra.Command, args []string) { 19 | networks := functions.GetNetworks() 20 | switch commons.OutputFormat { 21 | case commons.JsonOutput: 22 | functions.PrettyPrint(networks) 23 | default: 24 | table := tablewriter.NewWriter(os.Stdout) 25 | table.SetHeader([]string{"NetId", "Address Range (IPv4)", "Address Range (IPv6)", "Network Last Modified", "Nodes Last Modified"}) 26 | for _, n := range *networks { 27 | networkLastModified := time.Unix(n.NetworkLastModified, 0).Format(time.RFC3339) 28 | nodesLastModified := time.Unix(n.NodesLastModified, 0).Format(time.RFC3339) 29 | table.Append([]string{n.NetID, n.AddressRange, n.AddressRange6, networkLastModified, nodesLastModified}) 30 | } 31 | table.Render() 32 | } 33 | }, 34 | } 35 | 36 | func init() { 37 | rootCmd.AddCommand(networkListCmd) 38 | } 39 | -------------------------------------------------------------------------------- /cli/cmd/network/node_limit.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "log" 5 | "strconv" 6 | 7 | "github.com/gravitl/netmaker/cli/functions" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var networkNodeLimitCmd = &cobra.Command{ 12 | Use: "node_limit [NETWORK NAME] [NEW LIMIT]", 13 | Short: "Update network nodel limit", 14 | Long: `Update network nodel limit`, 15 | Args: cobra.ExactArgs(2), 16 | Run: func(cmd *cobra.Command, args []string) { 17 | nodelimit, err := strconv.ParseInt(args[1], 10, 32) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | functions.PrettyPrint(functions.UpdateNetworkNodeLimit(args[0], int32(nodelimit))) 22 | }, 23 | } 24 | 25 | func init() { 26 | rootCmd.AddCommand(networkNodeLimitCmd) 27 | } 28 | -------------------------------------------------------------------------------- /cli/cmd/network/root.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "network", 12 | Short: "Manage Netmaker Networks", 13 | Long: `Manage Netmaker Networks`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/node/create_egress.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/gravitl/netmaker/models" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var nodeCreateEgressCmd = &cobra.Command{ 12 | Use: "create_egress [NETWORK NAME] [NODE ID] [EGRESS GATEWAY ADDRESSES (comma separated)]", 13 | Args: cobra.ExactArgs(3), 14 | Short: "Turn a Node into a Egress", 15 | Long: `Turn a Node into a Egress`, 16 | Run: func(cmd *cobra.Command, args []string) { 17 | egress := &models.EgressGatewayRequest{ 18 | NetID: args[0], 19 | NodeID: args[1], 20 | Ranges: strings.Split(args[2], ","), 21 | } 22 | if natEnabled { 23 | egress.NatEnabled = "yes" 24 | } 25 | functions.PrettyPrint(functions.CreateEgress(args[0], args[1], egress)) 26 | }, 27 | } 28 | 29 | func init() { 30 | nodeCreateEgressCmd.Flags().BoolVar(&natEnabled, "nat", false, "Enable NAT for Egress Traffic ?") 31 | rootCmd.AddCommand(nodeCreateEgressCmd) 32 | } 33 | -------------------------------------------------------------------------------- /cli/cmd/node/create_ingress.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var nodeCreateIngressCmd = &cobra.Command{ 9 | Use: "create_remote_access_gateway [NETWORK NAME] [NODE ID]", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Turn a Node into a Remote Access Gateway (Ingress)", 12 | Long: `Turn a Node into a Remote Access Gateway (Ingress) for a Network.`, 13 | Deprecated: "in favour of the `gateway` subcommand, in Netmaker v0.90.0.", 14 | Aliases: []string{"create_rag"}, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | functions.PrettyPrint(functions.CreateIngress(args[0], args[1], failover)) 17 | }, 18 | } 19 | 20 | func init() { 21 | nodeCreateIngressCmd.Flags().BoolVar(&failover, "failover", false, "Enable FailOver ?") 22 | rootCmd.AddCommand(nodeCreateIngressCmd) 23 | } 24 | -------------------------------------------------------------------------------- /cli/cmd/node/create_relay.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var hostCreateRelayCmd = &cobra.Command{ 11 | Use: "create_relay [NETWORK][NODE ID] [RELAYED NODE IDS (comma separated)]", 12 | Args: cobra.ExactArgs(3), 13 | Short: "Turn a Node into a Relay", 14 | Long: `Turn a Node into a Relay`, 15 | Deprecated: "in favour of the `gateway` subcommand, in Netmaker v0.90.0.", 16 | Run: func(cmd *cobra.Command, args []string) { 17 | functions.PrettyPrint(functions.CreateRelay(args[0], args[1], strings.Split(args[2], ","))) 18 | }, 19 | } 20 | 21 | func init() { 22 | rootCmd.AddCommand(hostCreateRelayCmd) 23 | } 24 | -------------------------------------------------------------------------------- /cli/cmd/node/delete.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var force bool 9 | 10 | var nodeDeleteCmd = &cobra.Command{ 11 | Use: "delete [NETWORK NAME] [NODE ID]", 12 | Args: cobra.ExactArgs(2), 13 | Short: "Delete a Node", 14 | Long: `Delete a Node`, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | functions.PrettyPrint(functions.DeleteNode(args[0], args[1], force)) 17 | }, 18 | } 19 | 20 | func init() { 21 | rootCmd.AddCommand(nodeDeleteCmd) 22 | nodeDeleteCmd.PersistentFlags().BoolVarP(&force, "force", "f", false, "force delete a node") 23 | } 24 | -------------------------------------------------------------------------------- /cli/cmd/node/delete_egress.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var nodeDeleteEgressCmd = &cobra.Command{ 9 | Use: "delete_egress [NETWORK NAME] [NODE ID]", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Delete Egress role from a Node", 12 | Long: `Delete Egress role from a Node`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.DeleteEgress(args[0], args[1])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(nodeDeleteEgressCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/node/delete_ingress.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var nodeDeleteIngressCmd = &cobra.Command{ 9 | Use: "delete_remote_access_gateway [NETWORK NAME] [NODE ID]", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Delete Remote Access Gateway role from a Node", 12 | Long: `Delete Remote Access Gateway role from a Node`, 13 | Deprecated: "in favour of the `gateway` subcommand, in Netmaker v0.90.0.", 14 | Aliases: []string{"delete_rag"}, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | functions.PrettyPrint(functions.DeleteIngress(args[0], args[1])) 17 | }, 18 | } 19 | 20 | func init() { 21 | rootCmd.AddCommand(nodeDeleteIngressCmd) 22 | } 23 | -------------------------------------------------------------------------------- /cli/cmd/node/delete_relay.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var hostDeleteRelayCmd = &cobra.Command{ 9 | Use: "delete_relay [NETWORK] [NODE ID]", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Delete Relay from a node", 12 | Long: `Delete Relay from a node`, 13 | Deprecated: "in favour of the `gateway` subcommand, in Netmaker v0.90.0.", 14 | Run: func(cmd *cobra.Command, args []string) { 15 | functions.PrettyPrint(functions.DeleteRelay(args[0], args[1])) 16 | }, 17 | } 18 | 19 | func init() { 20 | rootCmd.AddCommand(hostDeleteRelayCmd) 21 | } 22 | -------------------------------------------------------------------------------- /cli/cmd/node/flags.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | var ( 4 | natEnabled bool 5 | failover bool 6 | networkName string 7 | nodeDefinitionFilePath string 8 | address string 9 | address6 string 10 | localAddress string 11 | name string 12 | postUp string 13 | postDown string 14 | relayedNodes string 15 | egressGatewayRanges string 16 | expirationDateTime int 17 | defaultACL bool 18 | dnsOn bool 19 | disconnect bool 20 | ) 21 | -------------------------------------------------------------------------------- /cli/cmd/node/get.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var nodeGetCmd = &cobra.Command{ 9 | Use: "get [NETWORK NAME] [NODE ID]", 10 | Args: cobra.ExactArgs(2), 11 | Short: "Get a node by ID", 12 | Long: `Get a node by ID`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetNodeByID(args[0], args[1])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(nodeGetCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/node/list.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | 7 | "github.com/gravitl/netmaker/cli/cmd/commons" 8 | "github.com/gravitl/netmaker/cli/functions" 9 | "github.com/gravitl/netmaker/models" 10 | "github.com/guumaster/tablewriter" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // nodeListCmd lists all nodes 15 | var nodeListCmd = &cobra.Command{ 16 | Use: "list", 17 | Args: cobra.NoArgs, 18 | Short: "List all nodes", 19 | Long: `List all nodes`, 20 | Run: func(cmd *cobra.Command, args []string) { 21 | var data []models.ApiNode 22 | if networkName != "" { 23 | data = *functions.GetNodes(networkName) 24 | } else { 25 | data = *functions.GetNodes() 26 | } 27 | switch commons.OutputFormat { 28 | case commons.JsonOutput: 29 | functions.PrettyPrint(data) 30 | default: 31 | table := tablewriter.NewWriter(os.Stdout) 32 | table.SetHeader([]string{"ID", "Addresses", "Network", "Egress", "Remote Access Gateway", "Relay"}) 33 | for _, d := range data { 34 | addresses := "" 35 | if d.Address != "" { 36 | addresses += d.Address 37 | } 38 | if d.Address6 != "" { 39 | if d.Address != "" { 40 | addresses += ", " 41 | } 42 | addresses += d.Address6 43 | } 44 | table.Append([]string{d.ID, addresses, d.Network, 45 | strconv.FormatBool(d.IsEgressGateway), strconv.FormatBool(d.IsIngressGateway), strconv.FormatBool(d.IsRelay)}) 46 | } 47 | table.Render() 48 | } 49 | }, 50 | } 51 | 52 | func init() { 53 | nodeListCmd.Flags().StringVar(&networkName, "network", "", "Network name specifier") 54 | rootCmd.AddCommand(nodeListCmd) 55 | } 56 | -------------------------------------------------------------------------------- /cli/cmd/node/root.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "node", 12 | Short: "Manage nodes associated with a network", 13 | Long: `Manage nodes associated with a network`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/cmd/gateway" 5 | "os" 6 | 7 | "github.com/gravitl/netmaker/cli/cmd/acl" 8 | "github.com/gravitl/netmaker/cli/cmd/commons" 9 | "github.com/gravitl/netmaker/cli/cmd/context" 10 | "github.com/gravitl/netmaker/cli/cmd/dns" 11 | "github.com/gravitl/netmaker/cli/cmd/enrollment_key" 12 | "github.com/gravitl/netmaker/cli/cmd/ext_client" 13 | "github.com/gravitl/netmaker/cli/cmd/failover" 14 | "github.com/gravitl/netmaker/cli/cmd/host" 15 | "github.com/gravitl/netmaker/cli/cmd/metrics" 16 | "github.com/gravitl/netmaker/cli/cmd/network" 17 | "github.com/gravitl/netmaker/cli/cmd/node" 18 | "github.com/gravitl/netmaker/cli/cmd/server" 19 | "github.com/gravitl/netmaker/cli/cmd/user" 20 | "github.com/spf13/cobra" 21 | ) 22 | 23 | // rootCmd represents the base command when called without any subcommands 24 | var rootCmd = &cobra.Command{ 25 | Use: "nmctl", 26 | Short: "CLI for interacting with Netmaker Server", 27 | Long: `CLI for interacting with Netmaker Server`, 28 | } 29 | 30 | // GetRoot returns the root of all subcommands 31 | func GetRoot() *cobra.Command { 32 | return rootCmd 33 | } 34 | 35 | // Execute adds all child commands to the root command and sets flags appropriately. 36 | // This is called by main.main(). It only needs to happen once to the rootCmd. 37 | func Execute() { 38 | err := rootCmd.Execute() 39 | if err != nil { 40 | os.Exit(1) 41 | } 42 | } 43 | 44 | func init() { 45 | rootCmd.PersistentFlags().StringVarP(&commons.OutputFormat, "output", "o", "", "List output in specific format (Enum:- json)") 46 | // Bind subcommands here 47 | rootCmd.AddCommand(network.GetRoot()) 48 | rootCmd.AddCommand(context.GetRoot()) 49 | rootCmd.AddCommand(acl.GetRoot()) 50 | rootCmd.AddCommand(node.GetRoot()) 51 | rootCmd.AddCommand(dns.GetRoot()) 52 | rootCmd.AddCommand(server.GetRoot()) 53 | rootCmd.AddCommand(ext_client.GetRoot()) 54 | rootCmd.AddCommand(user.GetRoot()) 55 | rootCmd.AddCommand(metrics.GetRoot()) 56 | rootCmd.AddCommand(host.GetRoot()) 57 | rootCmd.AddCommand(enrollment_key.GetRoot()) 58 | rootCmd.AddCommand(failover.GetRoot()) 59 | rootCmd.AddCommand(gateway.GetRoot()) 60 | } 61 | -------------------------------------------------------------------------------- /cli/cmd/server/config.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var serverConfigCmd = &cobra.Command{ 9 | Use: "config", 10 | Args: cobra.NoArgs, 11 | Short: "Retrieve server config", 12 | Long: `Retrieve server config`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetServerConfig()) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(serverConfigCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/server/has_admin.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var serverHasAdminCmd = &cobra.Command{ 11 | Use: "has_admin", 12 | Args: cobra.NoArgs, 13 | Short: "Check if server has an admin", 14 | Long: `Check if server has an admin`, 15 | Run: func(cmd *cobra.Command, args []string) { 16 | fmt.Println(*functions.HasAdmin()) 17 | }, 18 | } 19 | 20 | func init() { 21 | rootCmd.AddCommand(serverHasAdminCmd) 22 | } 23 | -------------------------------------------------------------------------------- /cli/cmd/server/health.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var serverHealthCmd = &cobra.Command{ 9 | Use: "health", 10 | Args: cobra.NoArgs, 11 | Short: "View server health", 12 | Long: `View server health`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetServerHealth()) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(serverHealthCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/server/info.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var serverInfoCmd = &cobra.Command{ 9 | Use: "info", 10 | Args: cobra.NoArgs, 11 | Short: "Retrieve server information", 12 | Long: `Retrieve server information`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetServerInfo()) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(serverInfoCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/server/root.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "server", 12 | Short: "Get netmaker server information", 13 | Long: `Get netmaker server information`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/user/create.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/gravitl/netmaker/models" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var userCreateCmd = &cobra.Command{ 12 | Use: "create", 13 | Args: cobra.NoArgs, 14 | Short: "Create a new user", 15 | Long: `Create a new user`, 16 | Run: func(cmd *cobra.Command, args []string) { 17 | user := &models.User{UserName: username, Password: password, PlatformRoleID: models.UserRoleID(platformID)} 18 | if len(networkRoles) > 0 { 19 | netRolesMap := make(map[models.NetworkID]map[models.UserRoleID]struct{}) 20 | for netID, netRoles := range networkRoles { 21 | roleMap := make(map[models.UserRoleID]struct{}) 22 | for _, roleID := range strings.Split(netRoles, " ") { 23 | roleMap[models.UserRoleID(roleID)] = struct{}{} 24 | } 25 | netRolesMap[models.NetworkID(netID)] = roleMap 26 | } 27 | user.NetworkRoles = netRolesMap 28 | } 29 | if len(groups) > 0 { 30 | grMap := make(map[models.UserGroupID]struct{}) 31 | for _, groupID := range groups { 32 | grMap[models.UserGroupID(groupID)] = struct{}{} 33 | } 34 | user.UserGroups = grMap 35 | } 36 | 37 | functions.PrettyPrint(functions.CreateUser(user)) 38 | }, 39 | } 40 | 41 | func init() { 42 | 43 | userCreateCmd.Flags().StringVar(&username, "name", "", "Name of the user") 44 | userCreateCmd.Flags().StringVar(&password, "password", "", "Password of the user") 45 | userCreateCmd.Flags().StringVarP(&platformID, "platform-role", "r", models.ServiceUser.String(), 46 | "Platform Role of the user; run `nmctl roles list` to see available user roles") 47 | userCreateCmd.MarkFlagRequired("name") 48 | userCreateCmd.MarkFlagRequired("password") 49 | userCreateCmd.PersistentFlags().StringToStringVarP(&networkRoles, "network-roles", "n", nil, 50 | "Mapping of networkID and list of roles user will be part of (comma separated)") 51 | userCreateCmd.Flags().BoolVar(&admin, "admin", false, "Make the user an admin ? (deprecated v0.25.0 onwards)") 52 | userCreateCmd.Flags().StringArrayVarP(&groups, "groups", "g", nil, "List of user groups the user will be part of (comma separated)") 53 | rootCmd.AddCommand(userCreateCmd) 54 | } 55 | -------------------------------------------------------------------------------- /cli/cmd/user/delete.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var userDeleteCmd = &cobra.Command{ 9 | Use: "delete [USER NAME]", 10 | Args: cobra.ExactArgs(1), 11 | Short: "Delete a user", 12 | Long: `Delete a user`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(*functions.DeleteUser(args[0])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(userDeleteCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/user/flags.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | var ( 4 | username string 5 | password string 6 | platformID string 7 | admin bool 8 | networkRoles map[string]string 9 | groups []string 10 | ) 11 | -------------------------------------------------------------------------------- /cli/cmd/user/get.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/functions" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var userGetCmd = &cobra.Command{ 9 | Use: "get [USER NAME]", 10 | Args: cobra.ExactArgs(1), 11 | Short: "Get a user", 12 | Long: `Get a user`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | functions.PrettyPrint(functions.GetUser(args[0])) 15 | }, 16 | } 17 | 18 | func init() { 19 | rootCmd.AddCommand(userGetCmd) 20 | } 21 | -------------------------------------------------------------------------------- /cli/cmd/user/list.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "github.com/gravitl/netmaker/cli/cmd/commons" 8 | "github.com/gravitl/netmaker/cli/functions" 9 | "github.com/guumaster/tablewriter" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var userListCmd = &cobra.Command{ 14 | Use: "list", 15 | Args: cobra.NoArgs, 16 | Short: "List all users", 17 | Long: `List all users`, 18 | Run: func(cmd *cobra.Command, args []string) { 19 | data := functions.ListUsers() 20 | switch commons.OutputFormat { 21 | case commons.JsonOutput: 22 | functions.PrettyPrint(data) 23 | default: 24 | table := tablewriter.NewWriter(os.Stdout) 25 | table.SetHeader([]string{"Name", "Platform Role", "Groups"}) 26 | for _, d := range *data { 27 | g := []string{} 28 | for gID := range d.UserGroups { 29 | g = append(g, gID.String()) 30 | } 31 | table.Append([]string{d.UserName, d.PlatformRoleID.String(), strings.Join(g, ",")}) 32 | } 33 | table.Render() 34 | } 35 | }, 36 | } 37 | 38 | func init() { 39 | rootCmd.AddCommand(userListCmd) 40 | } 41 | -------------------------------------------------------------------------------- /cli/cmd/user/root.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "user", 12 | Short: "Manage users and permissions", 13 | Long: `Manage users and permissions`, 14 | } 15 | 16 | // GetRoot returns the root subcommand 17 | func GetRoot() *cobra.Command { 18 | return rootCmd 19 | } 20 | 21 | // Execute adds all child commands to the root command and sets flags appropriately. 22 | // This is called by main.main(). It only needs to happen once to the rootCmd. 23 | func Execute() { 24 | err := rootCmd.Execute() 25 | if err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/cmd/user/update.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/gravitl/netmaker/cli/functions" 7 | "github.com/gravitl/netmaker/models" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var userUpdateCmd = &cobra.Command{ 12 | Use: "update [USER NAME]", 13 | Args: cobra.ExactArgs(1), 14 | Short: "Update a user", 15 | Long: `Update a user`, 16 | Run: func(cmd *cobra.Command, args []string) { 17 | user := &models.User{UserName: args[0]} 18 | if platformID != "" { 19 | user.PlatformRoleID = models.UserRoleID(platformID) 20 | } 21 | if len(networkRoles) > 0 { 22 | netRolesMap := make(map[models.NetworkID]map[models.UserRoleID]struct{}) 23 | for netID, netRoles := range networkRoles { 24 | roleMap := make(map[models.UserRoleID]struct{}) 25 | for _, roleID := range strings.Split(netRoles, ",") { 26 | roleMap[models.UserRoleID(roleID)] = struct{}{} 27 | } 28 | netRolesMap[models.NetworkID(netID)] = roleMap 29 | } 30 | user.NetworkRoles = netRolesMap 31 | } 32 | if len(groups) > 0 { 33 | grMap := make(map[models.UserGroupID]struct{}) 34 | for _, groupID := range groups { 35 | grMap[models.UserGroupID(groupID)] = struct{}{} 36 | } 37 | user.UserGroups = grMap 38 | } 39 | functions.PrettyPrint(functions.UpdateUser(user)) 40 | }, 41 | } 42 | 43 | func init() { 44 | 45 | userUpdateCmd.Flags().StringVar(&password, "password", "", "Password of the user") 46 | userUpdateCmd.Flags().StringVarP(&platformID, "platform-role", "r", "", 47 | "Platform Role of the user; run `nmctl roles list` to see available user roles") 48 | userUpdateCmd.PersistentFlags().StringToStringVarP(&networkRoles, "network-roles", "n", nil, 49 | "Mapping of networkID and list of roles user will be part of (comma separated)") 50 | userUpdateCmd.Flags().BoolVar(&admin, "admin", false, "Make the user an admin ? (deprecated v0.25.0 onwards)") 51 | userUpdateCmd.Flags().StringArrayVarP(&groups, "groups", "g", nil, "List of user groups the user will be part of (comma separated)") 52 | rootCmd.AddCommand(userUpdateCmd) 53 | } 54 | -------------------------------------------------------------------------------- /cli/functions/acl.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gravitl/netmaker/logic/acls" 8 | ) 9 | 10 | // GetACL - fetch all ACLs associated with a network 11 | func GetACL(networkName string) *acls.ACLContainer { 12 | return request[acls.ACLContainer](http.MethodGet, fmt.Sprintf("/api/networks/%s/acls", networkName), nil) 13 | } 14 | 15 | // UpdateACL - update an ACL 16 | func UpdateACL(networkName string, payload *acls.ACLContainer) *acls.ACLContainer { 17 | return request[acls.ACLContainer](http.MethodPut, fmt.Sprintf("/api/networks/%s/acls/v2", networkName), payload) 18 | } 19 | -------------------------------------------------------------------------------- /cli/functions/dns.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gravitl/netmaker/models" 8 | ) 9 | 10 | // GetDNS - fetch all DNS entries 11 | func GetDNS() *[]models.DNSEntry { 12 | return request[[]models.DNSEntry](http.MethodGet, "/api/dns", nil) 13 | } 14 | 15 | // GetNodeDNS - fetch all Node DNS entires 16 | func GetNodeDNS(networkName string) *[]models.DNSEntry { 17 | return request[[]models.DNSEntry](http.MethodGet, fmt.Sprintf("/api/dns/adm/%s/nodes", networkName), nil) 18 | } 19 | 20 | // GetCustomDNS - fetch user defined DNS entriees 21 | func GetCustomDNS(networkName string) *[]models.DNSEntry { 22 | return request[[]models.DNSEntry](http.MethodGet, fmt.Sprintf("/api/dns/adm/%s/custom", networkName), nil) 23 | } 24 | 25 | // GetNetworkDNS - fetch DNS entries associated with a network 26 | func GetNetworkDNS(networkName string) *[]models.DNSEntry { 27 | return request[[]models.DNSEntry](http.MethodGet, "/api/dns/adm/"+networkName, nil) 28 | } 29 | 30 | // CreateDNS - create a DNS entry 31 | func CreateDNS(networkName string, payload *models.DNSEntry) *models.DNSEntry { 32 | return request[models.DNSEntry](http.MethodPost, "/api/dns/"+networkName, payload) 33 | } 34 | 35 | // PushDNS - push a DNS entry to CoreDNS 36 | func PushDNS() *string { 37 | return request[string](http.MethodPost, "/api/dns/adm/pushdns", nil) 38 | } 39 | 40 | // DeleteDNS - delete a DNS entry 41 | func DeleteDNS(networkName, domainName string) *string { 42 | return request[string](http.MethodDelete, fmt.Sprintf("/api/dns/%s/%s", networkName, domainName), nil) 43 | } 44 | -------------------------------------------------------------------------------- /cli/functions/enrollment_keys.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gravitl/netmaker/models" 7 | ) 8 | 9 | // CreateEnrollmentKey - create an enrollment key 10 | func CreateEnrollmentKey(key *models.APIEnrollmentKey) *models.EnrollmentKey { 11 | return request[models.EnrollmentKey](http.MethodPost, "/api/v1/enrollment-keys", key) 12 | } 13 | 14 | // GetEnrollmentKeys - gets all enrollment keys 15 | func GetEnrollmentKeys() *[]models.EnrollmentKey { 16 | return request[[]models.EnrollmentKey](http.MethodGet, "/api/v1/enrollment-keys", nil) 17 | } 18 | 19 | // DeleteEnrollmentKey - delete an enrollment key 20 | func DeleteEnrollmentKey(keyID string) { 21 | request[any](http.MethodDelete, "/api/v1/enrollment-keys/"+keyID, nil) 22 | } 23 | -------------------------------------------------------------------------------- /cli/functions/ext_client.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gravitl/netmaker/models" 8 | ) 9 | 10 | // GetAllExtClients - fetch all external clients 11 | func GetAllExtClients() *[]models.ExtClient { 12 | return request[[]models.ExtClient](http.MethodGet, "/api/extclients", nil) 13 | } 14 | 15 | // GetNetworkExtClients - fetch external clients associated with a network 16 | func GetNetworkExtClients(networkName string) *[]models.ExtClient { 17 | return request[[]models.ExtClient](http.MethodGet, "/api/extclients/"+networkName, nil) 18 | } 19 | 20 | // GetExtClient - fetch a single external client 21 | func GetExtClient(networkName, clientID string) *models.ExtClient { 22 | return request[models.ExtClient](http.MethodGet, fmt.Sprintf("/api/extclients/%s/%s", networkName, clientID), nil) 23 | } 24 | 25 | // GetExtClientConfig - fetch a wireguard config of an external client 26 | func GetExtClientConfig(networkName, clientID string) string { 27 | return get(fmt.Sprintf("/api/extclients/%s/%s/file", networkName, clientID)) 28 | } 29 | 30 | // GetExtClientConfig - auto fetch a client config 31 | func GetExtClientHAConfig(networkName string) string { 32 | return get(fmt.Sprintf("/api/v1/client_conf/%s", networkName)) 33 | } 34 | 35 | // CreateExtClient - create an external client 36 | func CreateExtClient(networkName, nodeID string, extClient models.CustomExtClient) { 37 | request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), extClient) 38 | } 39 | 40 | // DeleteExtClient - delete an external client 41 | func DeleteExtClient(networkName, clientID string) *models.SuccessResponse { 42 | return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/extclients/%s/%s", networkName, clientID), nil) 43 | } 44 | 45 | // UpdateExtClient - update an external client 46 | func UpdateExtClient(networkName, clientID string, payload *models.CustomExtClient) *models.ExtClient { 47 | return request[models.ExtClient](http.MethodPut, fmt.Sprintf("/api/extclients/%s/%s", networkName, clientID), payload) 48 | } 49 | -------------------------------------------------------------------------------- /cli/functions/failover.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gravitl/netmaker/models" 8 | ) 9 | 10 | // EnableNodeFailover - Enable failover for a given Node 11 | func EnableNodeFailover(nodeID string) *models.SuccessResponse { 12 | return request[models.SuccessResponse](http.MethodPost, fmt.Sprintf("/api/v1/node/%s/failover", nodeID), nil) 13 | } 14 | 15 | // DisableNodeFailover - Disable failover for a given Node 16 | func DisableNodeFailover(nodeID string) *models.SuccessResponse { 17 | return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/v1/node/%s/failover", nodeID), nil) 18 | } 19 | -------------------------------------------------------------------------------- /cli/functions/gateway.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gravitl/netmaker/models" 6 | "net/http" 7 | ) 8 | 9 | func CreateGateway(ingressRequest models.IngressRequest, relayRequest models.RelayRequest) *models.ApiNode { 10 | return request[models.ApiNode](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/gateway", relayRequest.NetID, relayRequest.NodeID), &models.CreateGwReq{ 11 | IngressRequest: ingressRequest, 12 | RelayRequest: relayRequest, 13 | }) 14 | } 15 | 16 | func DeleteGateway(networkID, nodeID string) *models.ApiNode { 17 | return request[models.ApiNode](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/gateway", networkID, nodeID), nil) 18 | } 19 | -------------------------------------------------------------------------------- /cli/functions/host.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gravitl/netmaker/models" 8 | ) 9 | 10 | type hostNetworksUpdatePayload struct { 11 | Networks []string `json:"networks"` 12 | } 13 | 14 | // GetHosts - fetch all host entries 15 | func GetHosts() *[]models.ApiHost { 16 | return request[[]models.ApiHost](http.MethodGet, "/api/hosts", nil) 17 | } 18 | 19 | // DeleteHost - delete a host 20 | func DeleteHost(hostID string, force bool) *models.ApiHost { 21 | return request[models.ApiHost](http.MethodDelete, fmt.Sprintf("/api/hosts/%s?force=%t", hostID, force), nil) 22 | } 23 | 24 | // UpdateHost - update a host 25 | func UpdateHost(hostID string, body *models.ApiHost) *models.ApiHost { 26 | return request[models.ApiHost](http.MethodPut, "/api/hosts/"+hostID, body) 27 | } 28 | 29 | // AddHostToNetwork - add a network to host 30 | func AddHostToNetwork(hostID, network string) *hostNetworksUpdatePayload { 31 | return request[hostNetworksUpdatePayload](http.MethodPost, "/api/hosts/"+hostID+"/networks/"+network, nil) 32 | } 33 | 34 | // DeleteHostFromNetwork - deletes a network from host 35 | func DeleteHostFromNetwork(hostID, network string) *hostNetworksUpdatePayload { 36 | return request[hostNetworksUpdatePayload](http.MethodDelete, "/api/hosts/"+hostID+"/networks/"+network, nil) 37 | } 38 | 39 | // CreateRelay - add relay to a node 40 | func CreateRelay(netID, nodeID string, relayedNodes []string) *models.ApiNode { 41 | return request[models.ApiNode](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/createrelay", netID, nodeID), &models.RelayRequest{ 42 | NodeID: nodeID, 43 | NetID: netID, 44 | RelayedNodes: relayedNodes, 45 | }) 46 | } 47 | 48 | // DeleteRelay - remove relay from a node 49 | func DeleteRelay(netID, nodeID string) *models.ApiNode { 50 | return request[models.ApiNode](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deleterelay", netID, nodeID), nil) 51 | } 52 | 53 | // RefreshKeys - refresh wireguard keys 54 | func RefreshKeys(hostID string) any { 55 | if hostID == "" { 56 | return request[any](http.MethodPut, "/api/hosts/keys", nil) 57 | } 58 | return request[any](http.MethodPut, fmt.Sprintf("/api/hosts/%s/keys", hostID), nil) 59 | 60 | } 61 | -------------------------------------------------------------------------------- /cli/functions/metrics.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gravitl/netmaker/models" 8 | ) 9 | 10 | // GetNodeMetrics - fetch a single node's metrics 11 | func GetNodeMetrics(networkName, nodeID string) *models.Metrics { 12 | return request[models.Metrics](http.MethodGet, fmt.Sprintf("/api/metrics/%s/%s", networkName, nodeID), nil) 13 | } 14 | 15 | // GetNetworkNodeMetrics - fetch an entire network's metrics 16 | func GetNetworkNodeMetrics(networkName string) *models.NetworkMetrics { 17 | return request[models.NetworkMetrics](http.MethodGet, "/api/metrics/"+networkName, nil) 18 | } 19 | 20 | // GetAllMetrics - fetch all metrics 21 | func GetAllMetrics() *models.NetworkMetrics { 22 | return request[models.NetworkMetrics](http.MethodGet, "/api/metrics", nil) 23 | } 24 | 25 | // GetNetworkExtMetrics - fetch external client metrics belonging to a network 26 | func GetNetworkExtMetrics(networkName string) *map[string]models.Metric { 27 | return request[map[string]models.Metric](http.MethodGet, "/api/metrics-ext/"+networkName, nil) 28 | } 29 | -------------------------------------------------------------------------------- /cli/functions/network.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gravitl/netmaker/models" 8 | ) 9 | 10 | // CreateNetwork - creates a network 11 | func CreateNetwork(payload *models.Network) *models.Network { 12 | return request[models.Network](http.MethodPost, "/api/networks", payload) 13 | } 14 | 15 | // UpdateNetwork - updates a network 16 | func UpdateNetwork(name string, payload *models.Network) *models.Network { 17 | return request[models.Network](http.MethodPut, "/api/networks/"+name, payload) 18 | } 19 | 20 | // UpdateNetworkNodeLimit - updates a network 21 | func UpdateNetworkNodeLimit(name string, nodeLimit int32) *models.Network { 22 | return request[models.Network](http.MethodPut, fmt.Sprintf("/api/networks/%s/nodelimit", name), &models.Network{ 23 | NodeLimit: nodeLimit, 24 | }) 25 | } 26 | 27 | // GetNetworks - fetch all networks 28 | func GetNetworks() *[]models.Network { 29 | return request[[]models.Network](http.MethodGet, "/api/networks", nil) 30 | } 31 | 32 | // GetNetwork - fetch a single network 33 | func GetNetwork(name string) *models.Network { 34 | return request[models.Network](http.MethodGet, "/api/networks/"+name, nil) 35 | } 36 | 37 | // DeleteNetwork - delete a network 38 | func DeleteNetwork(name string) *string { 39 | return request[string](http.MethodDelete, "/api/networks/"+name, nil) 40 | } 41 | -------------------------------------------------------------------------------- /cli/functions/node.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gravitl/netmaker/models" 8 | ) 9 | 10 | // GetNodes - fetch all nodes 11 | func GetNodes(networkName ...string) *[]models.ApiNode { 12 | if len(networkName) == 1 { 13 | return request[[]models.ApiNode](http.MethodGet, "/api/nodes/"+networkName[0], nil) 14 | } else { 15 | return request[[]models.ApiNode](http.MethodGet, "/api/nodes", nil) 16 | } 17 | } 18 | 19 | // GetNodeByID - fetch a single node by ID 20 | func GetNodeByID(networkName, nodeID string) *models.NodeGet { 21 | return request[models.NodeGet](http.MethodGet, fmt.Sprintf("/api/nodes/%s/%s", networkName, nodeID), nil) 22 | } 23 | 24 | // UpdateNode - update a single node 25 | func UpdateNode(networkName, nodeID string, node *models.ApiNode) *models.ApiNode { 26 | return request[models.ApiNode](http.MethodPut, fmt.Sprintf("/api/nodes/%s/%s", networkName, nodeID), node) 27 | } 28 | 29 | // DeleteNode - delete a node 30 | func DeleteNode(networkName, nodeID string, force bool) *models.SuccessResponse { 31 | return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s?force=%t", networkName, nodeID, force), nil) 32 | } 33 | 34 | // CreateEgress - turn a node into an egress 35 | func CreateEgress(networkName, nodeID string, payload *models.EgressGatewayRequest) *models.ApiNode { 36 | return request[models.ApiNode](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/creategateway", networkName, nodeID), payload) 37 | } 38 | 39 | // DeleteEgress - remove egress role from a node 40 | func DeleteEgress(networkName, nodeID string) *models.ApiNode { 41 | return request[models.ApiNode](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deletegateway", networkName, nodeID), nil) 42 | } 43 | 44 | // CreateIngress - turn a node into an ingress 45 | func CreateIngress(networkName, nodeID string, failover bool) *models.ApiNode { 46 | return request[models.ApiNode](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/createingress", networkName, nodeID), &struct { 47 | Failover bool `json:"failover"` 48 | }{Failover: failover}) 49 | } 50 | 51 | // DeleteIngress - remove ingress role from a node 52 | func DeleteIngress(networkName, nodeID string) *models.ApiNode { 53 | return request[models.ApiNode](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deleteingress", networkName, nodeID), nil) 54 | } 55 | -------------------------------------------------------------------------------- /cli/functions/pretty_print.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | ) 8 | 9 | // PrettyPrint - print JSON with indentation 10 | func PrettyPrint(data any) { 11 | body, err := json.MarshalIndent(data, "", " ") 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | fmt.Println(string(body)) 16 | } 17 | -------------------------------------------------------------------------------- /cli/functions/server.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "net/http" 5 | 6 | cfg "github.com/gravitl/netmaker/config" 7 | "github.com/gravitl/netmaker/models" 8 | ) 9 | 10 | // GetLogs - fetch Netmaker server logs 11 | func GetLogs() string { 12 | return get("/api/logs") 13 | } 14 | 15 | // GetServerInfo - fetch minimal server info 16 | func GetServerInfo() *models.ServerConfig { 17 | return request[models.ServerConfig](http.MethodGet, "/api/server/getserverinfo", nil) 18 | } 19 | 20 | // GetServerConfig - fetch entire server config including secrets 21 | func GetServerConfig() *cfg.ServerConfig { 22 | return request[cfg.ServerConfig](http.MethodGet, "/api/server/getconfig", nil) 23 | } 24 | 25 | // GetServerHealth - fetch server current health status 26 | func GetServerHealth() string { 27 | return get("/api/server/health") 28 | } 29 | -------------------------------------------------------------------------------- /cli/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/cli/cmd" 5 | ) 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /cli/samples/network.json: -------------------------------------------------------------------------------- 1 | { 2 | "addressrange": "10.120.130.0/24", 3 | "addressrange6": "", 4 | "netid": "test3", 5 | "defaultlistenport": 51821, 6 | "nodelimit": 999999999, 7 | "defaultpostup": "", 8 | "defaultpostdown": "", 9 | "defaultkeepalive": 20, 10 | "defaultinterface": "nm-test3", 11 | "accesskeys": [], 12 | "allowmanualsignup": "no", 13 | "isipv4": "yes", 14 | "isipv6": "no", 15 | "ispointtosite": "no", 16 | "defaultudpholepunch": "yes", 17 | "defaultextclientdns": "", 18 | "defaultmtu": 1280, 19 | "defaultacl": "yes", 20 | "prosettings": { 21 | "defaultaccesslevel": 3, 22 | "defaultusernodelimit": 0, 23 | "defaultuserclientlimit": 0, 24 | "allowedusers": [], 25 | "allowedgroups": [ 26 | "*" 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /compose/docker-compose-emqx.yml: -------------------------------------------------------------------------------- 1 | version: "3.4" 2 | 3 | services: 4 | mq: 5 | container_name: mq 6 | image: emqx/emqx:5.8.2 7 | env_file: ./netmaker.env 8 | restart: unless-stopped 9 | environment: 10 | - EMQX_NAME=emqx 11 | - EMQX_DASHBOARD__DEFAULT_PASSWORD=${MQ_PASSWORD} 12 | - EMQX_DASHBOARD__DEFAULT_USERNAME=${MQ_USERNAME} 13 | ports: 14 | - "1883:1883" # MQTT 15 | - "8883:8883" # SSL MQTT 16 | - "8083:8083" # Websockets 17 | - "8084:8084" # SSL Websockets 18 | - "18083:18083" # Dashboard/REST_API 19 | volumes: 20 | - emqx_data:/opt/emqx/data 21 | - emqx_etc:/opt/emqx/etc 22 | - emqx_logs:/opt/emqx/log 23 | - ./emqx.conf:/opt/emqx/data/configs/cluster.hocon 24 | volumes: 25 | emqx_data: { } # storage for emqx data 26 | emqx_etc: { } # storage for emqx etc 27 | emqx_logs: { } # storage for emqx logs 28 | -------------------------------------------------------------------------------- /compose/docker-compose.netclient.yml: -------------------------------------------------------------------------------- 1 | version: "3.4" 2 | 3 | services: 4 | netclient: 5 | container_name: netclient 6 | image: 'gravitl/netclient:v0.90.0' 7 | hostname: netmaker-1 8 | network_mode: host 9 | restart: on-failure 10 | environment: 11 | TOKEN: "TOKEN_VALUE" 12 | volumes: 13 | - /etc/netclient:/etc/netclient 14 | cap_add: 15 | - NET_ADMIN 16 | - NET_RAW 17 | - SYS_MODULE 18 | -------------------------------------------------------------------------------- /compose/docker-compose.pro.yml: -------------------------------------------------------------------------------- 1 | version: "3.4" 2 | 3 | services: 4 | 5 | prometheus: 6 | container_name: prometheus 7 | image: gravitl/netmaker-prometheus:latest 8 | env_file: ./netmaker.env 9 | environment: 10 | # config-dependant vars 11 | - NETMAKER_METRICS_TARGET=netmaker-exporter.${NM_DOMAIN} 12 | restart: always 13 | volumes: 14 | - prometheus_data:/prometheus 15 | depends_on: 16 | - netmaker 17 | 18 | grafana: 19 | container_name: grafana 20 | image: gravitl/netmaker-grafana:latest 21 | env_file: ./netmaker.env 22 | environment: 23 | # config-dependant vars 24 | # TODO unify with netmaker-exporter 25 | - PROMETHEUS_HOST=prometheus.${NM_DOMAIN} 26 | - NETMAKER_METRICS_TARGET=netmaker-exporter.${NM_DOMAIN} 27 | volumes: 28 | - grafana_data:/var/lib/grafana 29 | restart: always 30 | links: 31 | - prometheus 32 | depends_on: 33 | - prometheus 34 | - netmaker 35 | 36 | netmaker-exporter: 37 | container_name: netmaker-exporter 38 | image: gravitl/netmaker-exporter:latest 39 | env_file: ./netmaker.env 40 | environment: 41 | # config-dependant vars 42 | # TODO unify with grafana 43 | - PROMETHEUS_HOST=https://prometheus.${NM_DOMAIN} 44 | # The domain/host IP indicating the mq broker address 45 | - BROKER_ENDPOINT=wss://broker.${NM_DOMAIN} 46 | - API_PORT=${EXPORTER_API_PORT} 47 | restart: always 48 | depends_on: 49 | - netmaker 50 | 51 | volumes: 52 | prometheus_data: { } 53 | grafana_data: { } 54 | -------------------------------------------------------------------------------- /config/config_test.go: -------------------------------------------------------------------------------- 1 | // Environment file for getting variables 2 | // Currently the only thing it does is set the master password 3 | // Should probably have it take over functions from OS such as port and mongodb connection details 4 | // Reads from the config/environments/dev.yaml file by default 5 | package config 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | ) 11 | 12 | func Test_readConfig(t *testing.T) { 13 | tests := []struct { 14 | name string 15 | want *EnvironmentConfig 16 | wantErr bool 17 | }{ 18 | { 19 | "ensure development config parses", 20 | &EnvironmentConfig{}, 21 | false, 22 | }, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | got, err := ReadConfig("") 27 | if (err != nil) != tt.wantErr { 28 | t.Errorf("readConfig() error = %v, wantErr %v", err, tt.wantErr) 29 | return 30 | } 31 | if !reflect.DeepEqual(got, tt.want) { 32 | t.Errorf("readConfig() = %v, want %v", got, tt.want) 33 | } 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /config/environments/dev.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | apihost: "" # defaults to 127.0.0.1 or remote ip (SERVER_HOST) if DisableRemoteIPCheck is not set to true. SERVER_API_HOST if set 3 | apiport: "" # defaults to 8081 or HTTP_PORT (if set) 4 | masterkey: "" # defaults to 'secretkey' or MASTER_KEY (if set) 5 | allowedorigin: "" # defaults to '*' or CORS_ALLOWED_ORIGIN (if set) 6 | restbackend: "" # defaults to "on" or REST_BACKEND (if set) 7 | dnsmode: "" # defaults to "on" or DNS_MODE (if set) 8 | sqlconn: "" # defaults to "http://" or SQL_CONN (if set) 9 | disableremoteipcheck: "" # defaults to "false" or DISABLE_REMOTE_IP_CHECK (if set) 10 | version: "" # version of server 11 | rce: "" # defaults to "off" 12 | publicipservice: "" # defaults to "" or PUBLIC_IP_SERVICE (if set) 13 | -------------------------------------------------------------------------------- /controllers/config/dnsconfig/Corefile: -------------------------------------------------------------------------------- 1 | . { 2 | reload 15s 3 | hosts /root/dnsconfig/netmaker.hosts { 4 | fallthrough 5 | } 6 | forward . 8.8.8.8 8.8.4.4 7 | log 8 | } 9 | -------------------------------------------------------------------------------- /controllers/config/environments/dev.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | host: "localhost" 3 | apiport: "8081" 4 | masterkey: "" 5 | allowedorigin: "*" 6 | restbackend: true 7 | defaultnetname: "default" 8 | defaultnetrange: "10.10.10.0/24" 9 | createdefault: true 10 | -------------------------------------------------------------------------------- /controllers/debug.go: -------------------------------------------------------------------------------- 1 | //go:build debug 2 | 3 | package controller 4 | 5 | import ( 6 | "context" 7 | "net/http" 8 | _ "net/http/pprof" 9 | "os" 10 | "os/signal" 11 | 12 | "github.com/gravitl/netmaker/logger" 13 | ) 14 | 15 | func init() { 16 | srv := &http.Server{Addr: "0.0.0.0:6060", Handler: nil} 17 | go func() { 18 | logger.Log(0, "Debug mode active") 19 | err := srv.ListenAndServe() 20 | if err != nil { 21 | logger.Log(0, err.Error()) 22 | } 23 | c := make(chan os.Signal) 24 | 25 | // Relay os.Interrupt to our channel (os.Interrupt = CTRL+C) 26 | // Ignore other incoming signals 27 | signal.Notify(c, os.Interrupt) 28 | // Block main routine until a signal is received 29 | // As long as user doesn't press CTRL+C a message is not passed and our main routine keeps running 30 | <-c 31 | srv.Shutdown(context.TODO()) 32 | }() 33 | } 34 | -------------------------------------------------------------------------------- /controllers/files.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gorilla/mux" 7 | ) 8 | 9 | // @Summary Retrieve a file from the file server 10 | // @Router /meshclient/files/{filename} [get] 11 | // @Tags Meshclient 12 | // @Success 200 {body} file "file" 13 | // @Failure 404 {string} string "404 not found" 14 | func fileHandlers(r *mux.Router) { 15 | r.PathPrefix("/meshclient/files"). 16 | Handler(http.StripPrefix("/meshclient/files", http.FileServer(http.Dir("./meshclient/files")))) 17 | } 18 | -------------------------------------------------------------------------------- /controllers/ipservice.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | "strings" 8 | 9 | "github.com/gorilla/mux" 10 | 11 | "github.com/gravitl/netmaker/netclient/ncutils" 12 | ) 13 | 14 | func ipHandlers(r *mux.Router) { 15 | r.HandleFunc("/api/getip", http.HandlerFunc(getPublicIP)).Methods(http.MethodGet) 16 | } 17 | 18 | // @Summary Get the current public IP address. 19 | // @Router /api/getip [get] 20 | // @Tags IP Service 21 | // @Security oauth2 22 | // @Success 200 {string} string "The public IP address." 23 | // @Failure 400 {string} string "Invalid IP address or no IP found." 24 | func getPublicIP(w http.ResponseWriter, r *http.Request) { 25 | r.Header.Set("Connection", "close") 26 | ip, err := parseIP(r) 27 | if err != nil { 28 | w.WriteHeader(400) 29 | switch { 30 | case ip != "": 31 | _, _ = w.Write([]byte("ip is invalid: " + ip)) 32 | case ip == "": 33 | _, _ = w.Write([]byte("no ip found")) 34 | default: 35 | fmt.Println(err) 36 | } 37 | return 38 | } 39 | 40 | w.WriteHeader(200) 41 | _, _ = w.Write([]byte(ip)) 42 | } 43 | 44 | func parseIP(r *http.Request) (string, error) { 45 | // Get Public IP from header 46 | ip := r.Header.Get("X-REAL-IP") 47 | ipnet := net.ParseIP(ip) 48 | if ipnet != nil && !ncutils.IpIsPrivate(ipnet) { 49 | return ip, nil 50 | } 51 | 52 | // If above fails, get Public IP from other header instead 53 | forwardips := r.Header.Get("X-FORWARDED-FOR") 54 | iplist := strings.Split(forwardips, ",") 55 | for _, ip := range iplist { 56 | ipnet := net.ParseIP(ip) 57 | if ipnet != nil && !ncutils.IpIsPrivate(ipnet) { 58 | return ip, nil 59 | } 60 | } 61 | 62 | // If above also fails, get Public IP from Remote Address of request 63 | ip, _, err := net.SplitHostPort(r.RemoteAddr) 64 | if err != nil { 65 | return "", err 66 | } 67 | ipnet = net.ParseIP(ip) 68 | if ipnet != nil { 69 | if ncutils.IpIsPrivate(ipnet) { 70 | return ip, fmt.Errorf("ip is a private address") 71 | } 72 | return ip, nil 73 | } 74 | return "", fmt.Errorf("no ip found") 75 | } 76 | -------------------------------------------------------------------------------- /controllers/legacy.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gorilla/mux" 7 | "github.com/gravitl/netmaker/logger" 8 | "github.com/gravitl/netmaker/logic" 9 | ) 10 | 11 | func legacyHandlers(r *mux.Router) { 12 | r.HandleFunc("/api/v1/legacy/nodes", logic.SecurityCheck(true, http.HandlerFunc(wipeLegacyNodes))). 13 | Methods(http.MethodDelete) 14 | } 15 | 16 | // @Summary Delete all legacy nodes from DB. 17 | // @Router /api/v1/legacy/nodes [delete] 18 | // @Tags Nodes 19 | // @Security oauth2 20 | // @Success 200 {string} string "Wiped all legacy nodes." 21 | // @Failure 400 {object} models.ErrorResponse 22 | func wipeLegacyNodes(w http.ResponseWriter, r *http.Request) { 23 | // Set header 24 | w.Header().Set("Content-Type", "application/json") 25 | if err := logic.RemoveAllLegacyNodes(); err != nil { 26 | logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) 27 | logger.Log(0, "error occurred when removing legacy nodes", err.Error()) 28 | } 29 | logger.Log(0, r.Header.Get("user"), "wiped legacy nodes") 30 | logic.ReturnSuccessResponse(w, r, "wiped all legacy nodes") 31 | } 32 | -------------------------------------------------------------------------------- /controllers/limits.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gravitl/netmaker/database" 7 | "github.com/gravitl/netmaker/logic" 8 | "github.com/gravitl/netmaker/models" 9 | ) 10 | 11 | // limit consts 12 | const ( 13 | limitChoiceNetworks = iota 14 | limitChoiceUsers 15 | limitChoiceMachines 16 | limitChoiceIngress 17 | limitChoiceEgress 18 | ) 19 | 20 | func checkFreeTierLimits(limitChoice int, next http.Handler) http.HandlerFunc { 21 | return func(w http.ResponseWriter, r *http.Request) { 22 | var errorResponse = models.ErrorResponse{ 23 | Code: http.StatusForbidden, Message: "free tier limits exceeded on ", 24 | } 25 | 26 | if logic.FreeTier { // check that free tier limits not exceeded 27 | switch limitChoice { 28 | case limitChoiceNetworks: 29 | currentNetworks, err := logic.GetNetworks() 30 | if (err != nil && !database.IsEmptyRecord(err)) || 31 | len(currentNetworks) >= logic.NetworksLimit { 32 | errorResponse.Message += "networks" 33 | logic.ReturnErrorResponse(w, r, errorResponse) 34 | return 35 | } 36 | case limitChoiceUsers: 37 | users, err := logic.GetUsers() 38 | if (err != nil && !database.IsEmptyRecord(err)) || 39 | len(users) >= logic.UsersLimit { 40 | errorResponse.Message += "users" 41 | logic.ReturnErrorResponse(w, r, errorResponse) 42 | return 43 | } 44 | case limitChoiceMachines: 45 | hosts, hErr := logic.GetAllHosts() 46 | clients, cErr := logic.GetAllExtClients() 47 | if (hErr != nil && !database.IsEmptyRecord(hErr)) || 48 | (cErr != nil && !database.IsEmptyRecord(cErr)) || 49 | len(hosts)+len(clients) >= logic.MachinesLimit { 50 | errorResponse.Message += "machines" 51 | logic.ReturnErrorResponse(w, r, errorResponse) 52 | return 53 | } 54 | case limitChoiceIngress: 55 | ingresses, err := logic.GetAllIngresses() 56 | if (err != nil && !database.IsEmptyRecord(err)) || 57 | len(ingresses) >= logic.IngressesLimit { 58 | errorResponse.Message += "ingresses" 59 | logic.ReturnErrorResponse(w, r, errorResponse) 60 | return 61 | } 62 | case limitChoiceEgress: 63 | egresses, err := logic.GetAllEgresses() 64 | if (err != nil && !database.IsEmptyRecord(err)) || 65 | len(egresses) >= logic.EgressesLimit { 66 | errorResponse.Message += "egresses" 67 | logic.ReturnErrorResponse(w, r, errorResponse) 68 | return 69 | } 70 | } 71 | } 72 | 73 | next.ServeHTTP(w, r) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /controllers/logger.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | 8 | "github.com/gorilla/mux" 9 | "github.com/gravitl/netmaker/logger" 10 | "github.com/gravitl/netmaker/logic" 11 | ) 12 | 13 | func loggerHandlers(r *mux.Router) { 14 | r.HandleFunc("/api/logs", logic.SecurityCheck(true, http.HandlerFunc(getLogs))).Methods(http.MethodGet) 15 | } 16 | 17 | func getLogs(w http.ResponseWriter, r *http.Request) { 18 | var currentTime = time.Now().Format(logger.TimeFormatDay) 19 | var currentFilePath = fmt.Sprintf("data/netmaker.log.%s", currentTime) 20 | logger.DumpFile(currentFilePath) 21 | w.WriteHeader(http.StatusOK) 22 | w.Write([]byte(logger.Retrieve(currentFilePath))) 23 | } 24 | -------------------------------------------------------------------------------- /controllers/regex.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "errors" 5 | "regexp" 6 | ) 7 | 8 | var ( 9 | errInvalidExtClientPubKey = errors.New("incorrect client public key") 10 | errInvalidExtClientID = errors.New("node name must be alphanumderic and/or dashes and less that 15 chars") 11 | errInvalidExtClientExtraIP = errors.New("client extra ip must be a valid cidr") 12 | errInvalidExtClientDNS = errors.New("client dns must be a valid ip address") 13 | errDuplicateExtClientName = errors.New("duplicate client name") 14 | ) 15 | 16 | // allow only dashes and alphaneumeric for ext client and node names 17 | func validName(name string) bool { 18 | reg, err := regexp.Compile("^[a-zA-Z0-9-]+$") 19 | if err != nil { 20 | return false 21 | } 22 | if !reg.MatchString(name) { 23 | return false 24 | } 25 | if len(name) < 5 || len(name) > 32 { 26 | return false 27 | } 28 | return true 29 | } 30 | -------------------------------------------------------------------------------- /controllers/regex_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import "testing" 4 | 5 | // TestValidName tests the validName function 6 | func TestValidName(t *testing.T) { 7 | type args struct { 8 | Name string 9 | } 10 | tests := []struct { 11 | Name string 12 | Args args 13 | Want bool 14 | }{ 15 | { 16 | Name: "validName", 17 | Args: args{ 18 | Name: "TestvalidName", 19 | }, 20 | Want: true, 21 | }, 22 | { 23 | Name: "invalidName", 24 | Args: args{ 25 | Name: "Test*Name", 26 | }, 27 | Want: false, 28 | }, 29 | { 30 | Name: "longname", 31 | Args: args{ 32 | Name: "TestvalidNameTestvalidName", 33 | }, 34 | Want: true, 35 | }, 36 | { 37 | Name: "max length", 38 | Args: args{ 39 | Name: "123456789012345", 40 | }, 41 | Want: true, 42 | }, 43 | { 44 | Name: "min length", 45 | Args: args{ 46 | Name: "ama", 47 | }, 48 | Want: false, 49 | }, 50 | { 51 | Name: "toolong", 52 | Args: args{ 53 | Name: "123456789012345123123123123123123123123123123", 54 | }, 55 | Want: false, 56 | }, 57 | } 58 | for _, tt := range tests { 59 | t.Run(tt.Name, func(t *testing.T) { 60 | if got := validName(tt.Args.Name); got != tt.Want { 61 | t.Errorf("validName() = %v, want %v", got, tt.Want) 62 | } 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /controllers/response_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | 10 | "github.com/gravitl/netmaker/logic" 11 | "github.com/gravitl/netmaker/models" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestFormatError(t *testing.T) { 16 | response := logic.FormatError(errors.New("this is a sample error"), "badrequest") 17 | assert.Equal(t, http.StatusBadRequest, response.Code) 18 | assert.Equal(t, "this is a sample error", response.Message) 19 | } 20 | 21 | func TestReturnSuccessResponse(t *testing.T) { 22 | var response models.SuccessResponse 23 | handler := func(rw http.ResponseWriter, r *http.Request) { 24 | logic.ReturnSuccessResponse(rw, r, "This is a test message") 25 | } 26 | req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) 27 | w := httptest.NewRecorder() 28 | handler(w, req) 29 | resp := w.Result() 30 | assert.Equal(t, http.StatusOK, resp.StatusCode) 31 | assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) 32 | //body, err := ioutil.ReadAll(resp.Body) 33 | //assert.Nil(t, err) 34 | //t.Log(body, string(body)) 35 | err := json.NewDecoder(resp.Body).Decode(&response) 36 | assert.Nil(t, err) 37 | assert.Equal(t, http.StatusOK, response.Code) 38 | assert.Equal(t, "This is a test message", response.Message) 39 | } 40 | 41 | func TestReturnErrorResponse(t *testing.T) { 42 | var response, errMessage models.ErrorResponse 43 | errMessage.Code = http.StatusUnauthorized 44 | errMessage.Message = "You are not authorized to access this endpoint" 45 | handler := func(rw http.ResponseWriter, r *http.Request) { 46 | logic.ReturnErrorResponse(rw, r, errMessage) 47 | } 48 | req := httptest.NewRequest(http.MethodGet, "http://example.com", nil) 49 | w := httptest.NewRecorder() 50 | handler(w, req) 51 | resp := w.Result() 52 | assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) 53 | assert.Equal(t, "application/json", resp.Header.Get("Content-Type")) 54 | err := json.NewDecoder(resp.Body).Decode(&response) 55 | assert.Nil(t, err) 56 | assert.Equal(t, http.StatusUnauthorized, response.Code) 57 | assert.Equal(t, "You are not authorized to access this endpoint", response.Message) 58 | } 59 | -------------------------------------------------------------------------------- /controllers/test.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gravitl/netmaker/35ddfc7a90aaf2098d3e78a396c0c79b5f510672/controllers/test.db -------------------------------------------------------------------------------- /database/statics.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | ) 7 | 8 | // SetPeers - sets peers for a network 9 | func SetPeers(newPeers map[string]string, networkName string) bool { 10 | areEqual := PeersAreEqual(newPeers, networkName) 11 | if !areEqual { 12 | jsonData, err := json.Marshal(newPeers) 13 | if err != nil { 14 | return false 15 | } 16 | InsertPeer(networkName, string(jsonData)) 17 | return true 18 | } 19 | return !areEqual 20 | } 21 | 22 | // GetPeers - gets peers for a given network 23 | func GetPeers(networkName string) (map[string]string, error) { 24 | record, err := FetchRecord(PEERS_TABLE_NAME, networkName) 25 | if err != nil && !IsEmptyRecord(err) { 26 | return nil, err 27 | } 28 | currentDataMap := make(map[string]string) 29 | if IsEmptyRecord(err) { 30 | return currentDataMap, nil 31 | } 32 | err = json.Unmarshal([]byte(record), ¤tDataMap) 33 | return currentDataMap, err 34 | } 35 | 36 | // PeersAreEqual - checks if peers are the same 37 | func PeersAreEqual(toCompare map[string]string, networkName string) bool { 38 | currentDataMap, err := GetPeers(networkName) 39 | if err != nil { 40 | return false 41 | } 42 | if len(currentDataMap) != len(toCompare) { 43 | return false 44 | } 45 | for k := range currentDataMap { 46 | if toCompare[k] != currentDataMap[k] { 47 | return false 48 | } 49 | } 50 | return true 51 | } 52 | 53 | // IsEmptyRecord - checks for if it's an empty record error or not 54 | func IsEmptyRecord(err error) bool { 55 | if err == nil { 56 | return false 57 | } 58 | return strings.Contains(err.Error(), NO_RECORD) || strings.Contains(err.Error(), NO_RECORDS) 59 | } 60 | -------------------------------------------------------------------------------- /dev.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | corednsaddr: "" 3 | apiconn: "api.ping.clustercat.com:443" 4 | apihost: "" 5 | apiport: "8081" 6 | mqhost: "localhost" 7 | masterkey: "secretkey" 8 | dnskey: "" 9 | allowedorigin: "*" 10 | nodeid: "netmaker" 11 | restbackend: "on" 12 | messagequeuebackend: "on" 13 | dnsmode: "on" 14 | disableremoteipcheck: "" 15 | grpcssl: "on" 16 | version: "" 17 | sqlconn: "" 18 | platform: "" 19 | database: "sqlite" 20 | verbosity: 3 21 | authprovider: "" 22 | clientid: "" 23 | clientsecret: "" 24 | frontendurl: "" 25 | displaykeys: "" 26 | azuretenant: "" 27 | telemetry: "" 28 | manageiptables: "off" 29 | portforwardservices: "" 30 | hostnetwork: "off" 31 | mqport: "" 32 | server: "broker.ping.clustercat.com" 33 | -------------------------------------------------------------------------------- /docker/Caddyfile: -------------------------------------------------------------------------------- 1 | # Dashboard 2 | https://dashboard.{$NM_DOMAIN} { 3 | # Apply basic security headers 4 | header { 5 | # Enable cross origin access to *.{$NM_DOMAIN} 6 | Access-Control-Allow-Origin *.{$NM_DOMAIN} 7 | # Enable HTTP Strict Transport Security (HSTS) 8 | Strict-Transport-Security "max-age=31536000;" 9 | # Enable cross-site filter (XSS) and tell browser to block detected attacks 10 | X-XSS-Protection "1; mode=block" 11 | # Disallow the site to be rendered within a frame on a foreign domain (clickjacking protection) 12 | X-Frame-Options "SAMEORIGIN" 13 | # Prevent search engines from indexing 14 | X-Robots-Tag "none" 15 | # Remove the server name 16 | -Server 17 | } 18 | 19 | reverse_proxy http://netmaker-ui 20 | } 21 | 22 | # API 23 | https://api.{$NM_DOMAIN} { 24 | reverse_proxy http://netmaker:8081 25 | } 26 | 27 | # MQ 28 | broker.{$NM_DOMAIN} { 29 | @ws { 30 | header Connection *Upgrade* 31 | header Upgrade websocket 32 | } 33 | reverse_proxy @ws mq:8883 # For EMQX websockets use `reverse_proxy @ws mq:8083` 34 | } 35 | -------------------------------------------------------------------------------- /docker/Caddyfile-pro: -------------------------------------------------------------------------------- 1 | # Dashboard 2 | https://dashboard.{$NM_DOMAIN} { 3 | # Apply basic security headers 4 | header { 5 | # Enable cross origin access to *.{$NM_DOMAIN} 6 | Access-Control-Allow-Origin *.{$NM_DOMAIN} 7 | # Enable HTTP Strict Transport Security (HSTS) 8 | Strict-Transport-Security "max-age=31536000;" 9 | # Enable cross-site filter (XSS) and tell browser to block detected attacks 10 | X-XSS-Protection "1; mode=block" 11 | # Disallow the site to be rendered within a frame on a foreign domain (clickjacking protection) 12 | X-Frame-Options "SAMEORIGIN" 13 | # Prevent search engines from indexing 14 | X-Robots-Tag "none" 15 | # Remove the server name 16 | -Server 17 | } 18 | 19 | reverse_proxy http://netmaker-ui 20 | } 21 | 22 | # Netmaker Exporter 23 | https://netmaker-exporter.{$NM_DOMAIN} { 24 | reverse_proxy http://netmaker-exporter:8085 25 | } 26 | 27 | # Prometheus 28 | https://prometheus.{$NM_DOMAIN} { 29 | reverse_proxy http://prometheus:9090 30 | } 31 | 32 | # Grafana 33 | https://grafana.{$NM_DOMAIN} { 34 | reverse_proxy http://grafana:3000 35 | } 36 | 37 | # API 38 | https://api.{$NM_DOMAIN} { 39 | reverse_proxy http://netmaker:8081 40 | } 41 | 42 | # MQ 43 | broker.{$NM_DOMAIN} { 44 | @ws { 45 | header Connection *Upgrade* 46 | header Upgrade websocket 47 | } 48 | reverse_proxy @ws mq:8883 49 | } 50 | -------------------------------------------------------------------------------- /docker/Dockerfile-go-builder: -------------------------------------------------------------------------------- 1 | FROM golang:1.23.0-alpine3.20 2 | ARG version 3 | RUN apk add build-base 4 | WORKDIR /app 5 | COPY go.* ./ 6 | RUN go mod download 7 | -------------------------------------------------------------------------------- /docker/Dockerfile-netclient-doks: -------------------------------------------------------------------------------- 1 | FROM debian:buster as builder 2 | # add glib support daemon manager 3 | 4 | RUN apt update -y && apt install -y wget bash gcc musl-dev openssl golang git build-essential libmnl-dev iptables 5 | 6 | RUN wget -O go.tgz https://go.dev/dl/go1.19.linux-amd64.tar.gz 7 | 8 | RUN tar -C /usr/local -xzf go.tgz 9 | 10 | WORKDIR /usr/local/go/src 11 | 12 | RUN chmod +x make.bash 13 | 14 | RUN ./make.bash 15 | 16 | ENV PATH="/usr/local/go/bin:$PATH" 17 | 18 | ENV GOPATH=/opt/go/ 19 | 20 | ENV PATH=$PATH:$GOPATH/bin 21 | 22 | WORKDIR /app 23 | 24 | COPY . . 25 | 26 | ENV GO111MODULE=auto 27 | 28 | RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 /usr/local/go/bin/go build -ldflags="-w -s" -o netclient-app netclient/main.go 29 | 30 | FROM debian:buster 31 | 32 | WORKDIR /root/ 33 | 34 | RUN apt update -y && apt install -y bash curl wget traceroute procps dnsutils iptables openresolv iproute2 35 | COPY --from=builder /app/netclient-app ./netclient 36 | COPY --from=builder /app/scripts/netclient.sh . 37 | RUN chmod 0755 netclient && chmod 0755 netclient.sh 38 | 39 | ENTRYPOINT ["/bin/bash", "./netclient.sh"] 40 | -------------------------------------------------------------------------------- /docker/Dockerfile-netclient-doks-uspace: -------------------------------------------------------------------------------- 1 | FROM debian:buster as builder 2 | # add glib support daemon manager 3 | 4 | RUN apt update -y && apt install -y wget bash gcc musl-dev openssl golang git build-essential libmnl-dev iptables 5 | 6 | RUN wget -O go.tgz https://go.dev/dl/go1.19.linux-amd64.tar.gz 7 | 8 | RUN tar -C /usr/local -xzf go.tgz 9 | 10 | WORKDIR /usr/local/go/src 11 | 12 | RUN chmod +x make.bash 13 | 14 | RUN ./make.bash 15 | 16 | ENV PATH="/usr/local/go/bin:$PATH" 17 | 18 | ENV GOPATH=/opt/go/ 19 | 20 | ENV PATH=$PATH:$GOPATH/bin 21 | 22 | WORKDIR /app 23 | 24 | COPY . . 25 | 26 | ENV GO111MODULE=auto 27 | 28 | RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 /usr/local/go/bin/go build -ldflags="-w -s" -o netclient-app netclient/main.go 29 | 30 | WORKDIR /root/ 31 | 32 | RUN git clone https://git.zx2c4.com/wireguard-go && \ 33 | cd wireguard-go && \ 34 | make && \ 35 | make install 36 | 37 | ENV WITH_WGQUICK=yes 38 | RUN git clone https://git.zx2c4.com/wireguard-tools && \ 39 | cd wireguard-tools && \ 40 | cd src && \ 41 | make && \ 42 | make install 43 | 44 | 45 | FROM debian:buster 46 | 47 | WORKDIR /root/ 48 | 49 | RUN apt update -y && apt install -y bash curl wget traceroute procps dnsutils iptables openresolv iproute2 50 | COPY --from=builder /usr/bin/wireguard-go /usr/bin/wg* /usr/bin/ 51 | COPY --from=builder /app/netclient-app ./netclient 52 | COPY --from=builder /app/scripts/netclient.sh . 53 | RUN chmod 0755 netclient && chmod 0755 netclient.sh 54 | 55 | ENV WG_QUICK_USERSPACE_IMPLEMENTATION=wireguard-go 56 | 57 | ENTRYPOINT ["/bin/bash", "./netclient.sh"] 58 | -------------------------------------------------------------------------------- /docker/Dockerfile-netclient-multiarch: -------------------------------------------------------------------------------- 1 | FROM gravitl/go-builder as builder 2 | # add glib support daemon manager 3 | WORKDIR /app 4 | ARG version 5 | 6 | COPY . . 7 | 8 | ENV GO111MODULE=auto 9 | 10 | RUN GOOS=linux CGO_ENABLED=0 /usr/local/go/bin/go build -ldflags="-X 'main.version=${version}'" -o netclient-app netclient/main.go 11 | 12 | FROM alpine:3.16.2 13 | 14 | WORKDIR /root/ 15 | 16 | RUN apk add --no-cache --update bash libmnl gcompat iptables openresolv iproute2 wireguard-tools 17 | COPY --from=builder /app/netclient-app ./netclient 18 | COPY --from=builder /app/scripts/netclient.sh . 19 | RUN chmod 0755 netclient && chmod 0755 netclient.sh 20 | 21 | 22 | ENTRYPOINT ["/bin/bash", "./netclient.sh"] 23 | -------------------------------------------------------------------------------- /docker/Dockerfile-netclient-multiarch-userspace: -------------------------------------------------------------------------------- 1 | FROM gravitl/go-builder:latest as builder 2 | # add glib support daemon manager 3 | WORKDIR /app 4 | 5 | COPY . . 6 | 7 | ENV GO111MODULE=auto 8 | 9 | RUN GOOS=linux CGO_ENABLED=0 /usr/local/go/bin/go build -ldflags="-w -s" -o netclient-app netclient/main.go 10 | 11 | WORKDIR /root/ 12 | 13 | RUN apk add --update git build-base libmnl-dev iptables 14 | 15 | RUN git clone https://git.zx2c4.com/wireguard-go && \ 16 | cd wireguard-go && \ 17 | make && \ 18 | make install 19 | 20 | ENV WITH_WGQUICK=yes 21 | RUN git clone https://git.zx2c4.com/wireguard-tools && \ 22 | cd wireguard-tools && \ 23 | cd src && \ 24 | make && \ 25 | make install 26 | 27 | FROM alpine:3.16.2 28 | 29 | WORKDIR /root/ 30 | 31 | RUN apk add --no-cache --update bash libmnl gcompat iptables openresolv iproute2 32 | COPY --from=builder /usr/bin/wireguard-go /usr/bin/wg* /usr/bin/ 33 | COPY --from=builder /app/netclient-app ./netclient 34 | COPY --from=builder /app/scripts/netclient.sh . 35 | RUN chmod 0755 netclient && chmod 0755 netclient.sh 36 | 37 | ENV WG_QUICK_USERSPACE_IMPLEMENTATION=wireguard-go 38 | 39 | ENTRYPOINT ["/bin/bash", "./netclient.sh"] 40 | -------------------------------------------------------------------------------- /docker/emqx.conf: -------------------------------------------------------------------------------- 1 | authentication = [ 2 | { 3 | backend = "built_in_database" 4 | mechanism = "password_based" 5 | password_hash_algorithm { 6 | name = "sha256", 7 | salt_position = "suffix" 8 | } 9 | user_id_type = "username" 10 | } 11 | ] 12 | authorization { 13 | deny_action = ignore 14 | no_match = allow 15 | sources = [ 16 | { 17 | type = built_in_database 18 | enable = true 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /docker/mosquitto.conf: -------------------------------------------------------------------------------- 1 | per_listener_settings false 2 | listener 8883 3 | protocol websockets 4 | allow_anonymous false 5 | 6 | listener 1883 7 | protocol websockets 8 | allow_anonymous false 9 | 10 | password_file /mosquitto/password.txt 11 | -------------------------------------------------------------------------------- /docker/wait.sh: -------------------------------------------------------------------------------- 1 | #!/bin/ash 2 | 3 | encrypt_password() { 4 | echo "${MQ_USERNAME}:${MQ_PASSWORD}" > /mosquitto/password.txt 5 | mosquitto_passwd -U /mosquitto/password.txt 6 | } 7 | 8 | main(){ 9 | 10 | encrypt_password 11 | echo "Starting MQ..." 12 | # Run the main container command. 13 | /docker-entrypoint.sh 14 | /usr/sbin/mosquitto -c /mosquitto/config/mosquitto.conf 15 | 16 | } 17 | 18 | main "${@}" 19 | -------------------------------------------------------------------------------- /docs/APIUsage.md: -------------------------------------------------------------------------------- 1 | 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. 2 | -------------------------------------------------------------------------------- /docs/Authentication.md: -------------------------------------------------------------------------------- 1 | API calls are primarily authenticated using a user authentication token. This token should be included in the header as follows: 2 | 3 | -H "Authorization: Bearer " 4 | 5 | To obtain YOUR_AUTH_TOKEN: 6 | Call the api/users/adm/authenticate endpoint (see documentation below for details). 7 | 8 | Note: While a MasterKey exists (configurable via env var or config file), it should be considered a backup option, used only when server access is lost. By default, this key is "secret key," but it's crucial to change this and keep it secure in your instance. 9 | 10 | For more information on configuration and security best practices, refer to the [Netmaker documentation](https://docs.netmaker.io/). 11 | -------------------------------------------------------------------------------- /docs/Pricing.md: -------------------------------------------------------------------------------- 1 | Check out our [Pricing](https://www.netmaker.io/pricing). And Feel Free to [Contact Us](https://www.netmaker.io/contact) if you have any questions or need some clarifications. 2 | -------------------------------------------------------------------------------- /functions/helpers.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | 7 | "github.com/gravitl/netmaker/database" 8 | "github.com/gravitl/netmaker/models" 9 | ) 10 | 11 | // NameInDNSCharSet - name in dns char set 12 | func NameInDNSCharSet(name string) bool { 13 | 14 | charset := "abcdefghijklmnopqrstuvwxyz1234567890-." 15 | 16 | for _, char := range name { 17 | if !strings.Contains(charset, strings.ToLower(string(char))) { 18 | return false 19 | } 20 | } 21 | return true 22 | } 23 | 24 | // NameInNodeCharSet - name in node char set 25 | func NameInNodeCharSet(name string) bool { 26 | 27 | charset := "abcdefghijklmnopqrstuvwxyz1234567890-" 28 | 29 | for _, char := range name { 30 | if !strings.Contains(charset, strings.ToLower(string(char))) { 31 | return false 32 | } 33 | } 34 | return true 35 | } 36 | 37 | // RemoveDeletedNode - remove deleted node 38 | func RemoveDeletedNode(nodeid string) bool { 39 | return database.DeleteRecord(database.DELETED_NODES_TABLE_NAME, nodeid) == nil 40 | } 41 | 42 | // GetAllExtClients - get all ext clients 43 | func GetAllExtClients() ([]models.ExtClient, error) { 44 | var extclients []models.ExtClient 45 | collection, err := database.FetchRecords(database.EXT_CLIENT_TABLE_NAME) 46 | 47 | if err != nil { 48 | return extclients, err 49 | } 50 | 51 | for _, value := range collection { 52 | var extclient models.ExtClient 53 | err := json.Unmarshal([]byte(value), &extclient) 54 | if err != nil { 55 | return []models.ExtClient{}, err 56 | } 57 | // add node to our array 58 | extclients = append(extclients, extclient) 59 | } 60 | 61 | return extclients, nil 62 | } 63 | -------------------------------------------------------------------------------- /functions/local.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/gravitl/netmaker/logger" 7 | "github.com/gravitl/netmaker/logic" 8 | ) 9 | 10 | // LINUX_APP_DATA_PATH - linux path 11 | const LINUX_APP_DATA_PATH = "/etc/netmaker" 12 | 13 | // FileExists - checks if file exists 14 | func FileExists(f string) bool { 15 | info, err := os.Stat(f) 16 | if os.IsNotExist(err) { 17 | return false 18 | } 19 | return !info.IsDir() 20 | } 21 | 22 | // SetDNSDir - sets the dns directory of the system 23 | func SetDNSDir() error { 24 | dir, err := os.Getwd() 25 | if err != nil { 26 | return err 27 | } 28 | 29 | err = os.MkdirAll(dir+"/config/dnsconfig", 0744) 30 | if err != nil { 31 | logger.Log(0, "couldnt find or create /config/dnsconfig") 32 | return err 33 | } 34 | 35 | err = logic.SetCorefile(".") 36 | if err != nil { 37 | logger.Log(0, err.Error()) 38 | } 39 | _, err = os.Stat(dir + "/config/dnsconfig/netmaker.hosts") 40 | if os.IsNotExist(err) { 41 | _, err = os.Create(dir + "/config/dnsconfig/netmaker.hosts") 42 | if err != nil { 43 | logger.Log(0, err.Error()) 44 | } 45 | } 46 | return nil 47 | } 48 | 49 | // GetNetmakerPath - gets netmaker path locally 50 | func GetNetmakerPath() string { 51 | return LINUX_APP_DATA_PATH 52 | } 53 | -------------------------------------------------------------------------------- /k8s/client/netclient-daemonset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: DaemonSet 3 | metadata: 4 | name: netclient 5 | labels: 6 | app: netclient 7 | spec: 8 | selector: 9 | matchLabels: 10 | app: netclient 11 | template: 12 | metadata: 13 | labels: 14 | app: netclient 15 | spec: 16 | hostNetwork: true 17 | containers: 18 | - name: netclient 19 | image: gravitl/netclient:v0.90.0 20 | env: 21 | - name: TOKEN 22 | value: "TOKEN_VALUE" 23 | volumeMounts: 24 | - mountPath: /etc/netclient 25 | name: etc-netclient 26 | - mountPath: /var/log 27 | name: log-netclient 28 | securityContext: 29 | privileged: true 30 | volumes: 31 | - hostPath: 32 | # change path for every netclient deployed, e.g. /etc/netclient-1, /etc/netclient-2, etc. 33 | # alternatively "netclient join" can be run from inside the container for new networks 34 | path: /etc/netclient 35 | type: DirectoryOrCreate 36 | name: etc-netclient 37 | - emptyDir: 38 | medium: Memory 39 | name: log-netclient 40 | -------------------------------------------------------------------------------- /k8s/client/netclient.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: netclient 6 | labels: 7 | app: netclient 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: netclient 12 | replicas: 1 13 | template: 14 | metadata: 15 | labels: 16 | app: netclient 17 | spec: 18 | hostNetwork: true 19 | # affinity: 20 | # nodeAffinity: 21 | # preferredDuringSchedulingIgnoredDuringExecution: 22 | # - weight: 1 23 | # preference: 24 | # matchExpressions: 25 | # - key: 26 | # operator: In 27 | # values: 28 | # - "" 29 | containers: 30 | - name: netclient 31 | image: gravitl/netclient:v0.90.0 32 | env: 33 | - name: TOKEN 34 | value: "TOKEN_VALUE" 35 | volumeMounts: 36 | - mountPath: /etc/netclient 37 | name: etc-netclient 38 | - mountPath: /var/log 39 | name: log-netclient 40 | securityContext: 41 | privileged: true 42 | volumes: 43 | - hostPath: 44 | path: /etc/netclient 45 | type: DirectoryOrCreate 46 | name: etc-netclient 47 | - emptyDir: 48 | medium: Memory 49 | name: log-netclient 50 | -------------------------------------------------------------------------------- /k8s/misc/clusterissuer.yaml: -------------------------------------------------------------------------------- 1 | # before applying, run the following: microk8s kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.4.0/cert-manager.yaml 2 | apiVersion: cert-manager.io/v1alpha2 3 | kind: ClusterIssuer 4 | metadata: 5 | name: letsencrypt-prod 6 | namespace: cert-manager 7 | spec: 8 | acme: 9 | # The ACME server URL 10 | server: https://acme-v02.api.letsencrypt.org/directory 11 | # Email address used for ACME registration 12 | email: YOUR_EMAIL 13 | # Name of a secret used to store the ACME account private key 14 | privateKeySecretRef: 15 | name: letsencrypt-prod 16 | # Enable the HTTP-01 challenge provider 17 | solvers: 18 | - http01: 19 | ingress: 20 | class: public 21 | -------------------------------------------------------------------------------- /k8s/misc/dnsutils.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: dnsutils 5 | spec: 6 | containers: 7 | - name: dnsutils 8 | image: gcr.io/kubernetes-e2e-test-images/dnsutils:1.3 9 | command: 10 | - sleep 11 | - "3600" 12 | imagePullPolicy: IfNotPresent 13 | restartPolicy: Always 14 | -------------------------------------------------------------------------------- /k8s/misc/nginx-example.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-deployment 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: nginx 9 | replicas: 5 # Update the replicas from 2 to 4 10 | template: 11 | metadata: 12 | labels: 13 | app: nginx 14 | spec: 15 | containers: 16 | - name: nginx 17 | image: nginx:1.14.2 18 | ports: 19 | - containerPort: 80 20 | affinity: 21 | nodeAffinity: 22 | preferredDuringSchedulingIgnoredDuringExecution: 23 | - weight: 1 24 | preference: 25 | matchExpressions: 26 | - key: location 27 | operator: In 28 | values: 29 | - onprem 30 | --- 31 | apiVersion: v1 32 | kind: Service 33 | metadata: 34 | labels: 35 | app: nginx 36 | name: nginx-service 37 | spec: 38 | ports: 39 | - port: 80 40 | protocol: TCP 41 | targetPort: 80 42 | selector: 43 | app: nginx 44 | sessionAffinity: None 45 | type: ClusterIP 46 | --- 47 | apiVersion: networking.k8s.io/v1 48 | kind: Ingress 49 | metadata: 50 | name: nginx-ingress 51 | annotations: 52 | nginx.ingress.kubernetes.io/rewrite-target: / 53 | cert-manager.io/cluster-issuer: "letsencrypt-prod" 54 | nginx.ingress.kubernetes.io/ssl-redirect: 'true' 55 | spec: 56 | ingressClassName: public 57 | tls: 58 | - hosts: 59 | - nginx.BASE_DOMAIN 60 | secretName: nginx-tls 61 | rules: 62 | - host: nginx.BASE_DOMAIN 63 | http: 64 | paths: 65 | - path: / 66 | pathType: Prefix 67 | backend: 68 | service: 69 | name: nginx-service 70 | port: 71 | number: 80 72 | 73 | 74 | -------------------------------------------------------------------------------- /k8s/misc/pingtest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: pingtest 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: pingtest 9 | replicas: 2 10 | template: 11 | metadata: 12 | labels: 13 | app: pingtest 14 | spec: 15 | affinity: 16 | podAntiAffinity: 17 | requiredDuringSchedulingIgnoredDuringExecution: 18 | - labelSelector: 19 | matchExpressions: 20 | - key: app 21 | operator: In 22 | values: 23 | - pingtest 24 | topologyKey: "kubernetes.io/hostname" 25 | containers: 26 | - name: busybox 27 | image: busybox 28 | command: ["/bin/sh", "-ec", "sleep 10000"] 29 | -------------------------------------------------------------------------------- /k8s/misc/ubuntu.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: ubuntu 5 | labels: 6 | app: ubuntu 7 | spec: 8 | # Uncomment and specify a specific node you want to debug 9 | # nodeName: 10 | containers: 11 | - image: ubuntu 12 | command: 13 | - "sleep" 14 | - "3600" 15 | imagePullPolicy: IfNotPresent 16 | name: ubuntu 17 | securityContext: 18 | privileged: true 19 | volumeMounts: 20 | - name: rootfolder 21 | mountPath: / 22 | restartPolicy: Never 23 | hostIPC: true 24 | hostNetwork: true 25 | hostPID: true 26 | volumes: 27 | - hostPath: 28 | path: / 29 | type: "" 30 | name: rootfolder 31 | -------------------------------------------------------------------------------- /k8s/server/netmaker-ui.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: netmaker-ui 6 | spec: 7 | replicas: 2 8 | selector: 9 | matchLabels: 10 | app: netmaker-ui 11 | template: 12 | metadata: 13 | labels: 14 | app: netmaker-ui 15 | spec: 16 | containers: 17 | - name: netmaker-ui 18 | image: gravitl/netmaker-ui:v0.90.0 19 | ports: 20 | - containerPort: 443 21 | env: 22 | - name: BACKEND_URL 23 | value: 'https://api.NETMAKER_SUBDOMAIN' 24 | terminationGracePeriodSeconds: 15 25 | --- 26 | apiVersion: v1 27 | kind: Service 28 | metadata: 29 | name: 'netmaker-ui' 30 | spec: 31 | ports: 32 | - port: 80 33 | protocol: TCP 34 | targetPort: 80 35 | selector: 36 | app: 'netmaker-ui' 37 | sessionAffinity: None 38 | type: 'ClusterIP' 39 | # --- 40 | # apiVersion: networking.k8s.io/v1 41 | # kind: Ingress 42 | # metadata: 43 | # name: nm-ui-ingress-nginx 44 | # annotations: 45 | # nginx.ingress.kubernetes.io/rewrite-target: / 46 | # cert-manager.io/cluster-issuer: "letsencrypt-nginx" 47 | # nginx.ingress.kubernetes.io/ssl-redirect: 'true' 48 | # spec: 49 | # ingressClassName: nginx 50 | # tls: 51 | # - hosts: 52 | # - dashboard.NETMAKER_SUBDOMAIN 53 | # secretName: nm-ui-tls 54 | # rules: 55 | # - host: dashboard.NETMAKER_SUBDOMAIN 56 | # http: 57 | # paths: 58 | # - path: / 59 | # pathType: Prefix 60 | # backend: 61 | # service: 62 | # name: netmaker-ui 63 | # port: 64 | # number: 80 65 | -------------------------------------------------------------------------------- /logger/types.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | type entry struct { 4 | Time string 5 | Count int 6 | } 7 | -------------------------------------------------------------------------------- /logger/util.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Verbosity - current logging verbosity level (optionally set) 8 | var Verbosity = 0 9 | 10 | // MakeString - makes a string using golang string builder 11 | func MakeString(delimeter string, message ...string) string { 12 | var builder strings.Builder 13 | for i := range message { 14 | builder.WriteString(message[i]) 15 | if delimeter != "" && i != len(message)-1 { 16 | builder.WriteString(delimeter) 17 | } 18 | } 19 | return builder.String() 20 | } 21 | 22 | func getVerbose() int32 { 23 | if Verbosity >= 1 && Verbosity <= 4 { 24 | return int32(Verbosity) 25 | } 26 | return int32(Verbosity) 27 | } 28 | -------------------------------------------------------------------------------- /logic/acls/nodeacls/retrieve.go: -------------------------------------------------------------------------------- 1 | package nodeacls 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "maps" 7 | "sync" 8 | 9 | "github.com/gravitl/netmaker/logic/acls" 10 | "github.com/gravitl/netmaker/servercfg" 11 | ) 12 | 13 | var NodesAllowedACLMutex = &sync.Mutex{} 14 | 15 | // AreNodesAllowed - checks if nodes are allowed to communicate in their network ACL 16 | func AreNodesAllowed(networkID NetworkID, node1, node2 NodeID) bool { 17 | if !servercfg.IsOldAclEnabled() { 18 | return true 19 | } 20 | NodesAllowedACLMutex.Lock() 21 | defer NodesAllowedACLMutex.Unlock() 22 | var currentNetworkACL, err = FetchAllACLs(networkID) 23 | if err != nil { 24 | return false 25 | } 26 | var allowed bool 27 | acls.AclMutex.Lock() 28 | currNetworkACLNode1 := currentNetworkACL[acls.AclID(node1)] 29 | currNetworkACLNode2 := currentNetworkACL[acls.AclID(node2)] 30 | acls.AclMutex.Unlock() 31 | allowed = currNetworkACLNode1.IsAllowed(acls.AclID(node2)) && currNetworkACLNode2.IsAllowed(acls.AclID(node1)) 32 | return allowed 33 | } 34 | 35 | // FetchNodeACL - fetches a specific node's ACL in a given network 36 | func FetchNodeACL(networkID NetworkID, nodeID NodeID) (acls.ACL, error) { 37 | var currentNetworkACL, err = FetchAllACLs(networkID) 38 | if err != nil { 39 | return nil, err 40 | } 41 | var acl acls.ACL 42 | acls.AclMutex.RLock() 43 | if currentNetworkACL[acls.AclID(nodeID)] == nil { 44 | acls.AclMutex.RUnlock() 45 | return nil, fmt.Errorf("no node ACL present for node %s", nodeID) 46 | } 47 | acl = currentNetworkACL[acls.AclID(nodeID)] 48 | acls.AclMutex.RUnlock() 49 | return acl, nil 50 | } 51 | 52 | // FetchNodeACLJson - fetches a node's acl in given network except returns the json string 53 | func FetchNodeACLJson(networkID NetworkID, nodeID NodeID) (acls.ACLJson, error) { 54 | currentNodeACL, err := FetchNodeACL(networkID, nodeID) 55 | if err != nil { 56 | return "", err 57 | } 58 | acls.AclMutex.RLock() 59 | defer acls.AclMutex.RUnlock() 60 | jsonData, err := json.Marshal(¤tNodeACL) 61 | if err != nil { 62 | return "", err 63 | } 64 | return acls.ACLJson(jsonData), nil 65 | } 66 | 67 | // FetchAllACLs - fetchs all node 68 | func FetchAllACLs(networkID NetworkID) (acls.ACLContainer, error) { 69 | var err error 70 | var currentNetworkACL acls.ACLContainer 71 | currentNetworkACL, err = currentNetworkACL.Get(acls.ContainerID(networkID)) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return maps.Clone(currentNetworkACL), nil 76 | } 77 | -------------------------------------------------------------------------------- /logic/acls/nodeacls/types.go: -------------------------------------------------------------------------------- 1 | package nodeacls 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/logic/acls" 5 | ) 6 | 7 | type ( 8 | // NodeACL - interface for NodeACLs 9 | NodeACL acls.ACL 10 | // NodeID - node ID for ACLs 11 | NodeID acls.AclID 12 | // NetworkID - ACL container based on network ID for nodes 13 | NetworkID acls.ContainerID 14 | ) 15 | -------------------------------------------------------------------------------- /logic/acls/types.go: -------------------------------------------------------------------------------- 1 | package acls 2 | 3 | var ( 4 | // NotPresent - 0 - not present (default) 5 | NotPresent = byte(0) 6 | // NotAllowed - 1 - not allowed access 7 | NotAllowed = byte(1) // 1 - not allowed 8 | // Allowed - 2 - allowed access 9 | Allowed = byte(2) 10 | ) 11 | 12 | type ( 13 | // AclID - the node id of a given node 14 | AclID string 15 | 16 | // ACL - the ACL of other nodes in a NetworkACL for a single unique node 17 | ACL map[AclID]byte 18 | 19 | // ACLJson - the string representation in JSON of an ACL Node or Network 20 | ACLJson string 21 | 22 | // ContainerID - the networkID of a given network 23 | ContainerID string 24 | 25 | // ACLContainer - the total list of all node's ACL in a given network 26 | ACLContainer map[AclID]ACL 27 | ) 28 | -------------------------------------------------------------------------------- /logic/clients.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "errors" 5 | "sort" 6 | 7 | "github.com/gravitl/netmaker/logic/acls" 8 | "github.com/gravitl/netmaker/models" 9 | "golang.org/x/exp/slog" 10 | ) 11 | 12 | // functions defined here, handle client ACLs, should be set on ee 13 | 14 | var ( 15 | // DenyClientNodeAccess - function to handle adding a node to an ext client's denied node set 16 | DenyClientNodeAccess = func(ec *models.ExtClient, clientOrNodeID string) bool { 17 | return true 18 | } 19 | // IsClientNodeAllowed - function to check if an ext client's denied node set contains a node ID 20 | IsClientNodeAllowed = func(ec *models.ExtClient, clientOrNodeID string) bool { 21 | return true 22 | } 23 | // AllowClientNodeAccess - function to handle removing a node ID from ext client's denied nodes, thus allowing it 24 | AllowClientNodeAccess = func(ec *models.ExtClient, clientOrNodeID string) bool { 25 | return true 26 | } 27 | SetClientDefaultACLs = func(ec *models.ExtClient) error { 28 | // allow all on CE 29 | networkAcls := acls.ACLContainer{} 30 | networkAcls, err := networkAcls.Get(acls.ContainerID(ec.Network)) 31 | if err != nil { 32 | slog.Error("failed to get network acls", "error", err) 33 | return err 34 | } 35 | networkAcls[acls.AclID(ec.ClientID)] = make(acls.ACL) 36 | for objId := range networkAcls { 37 | networkAcls[objId][acls.AclID(ec.ClientID)] = acls.Allowed 38 | networkAcls[acls.AclID(ec.ClientID)][objId] = acls.Allowed 39 | } 40 | delete(networkAcls[acls.AclID(ec.ClientID)], acls.AclID(ec.ClientID)) 41 | if _, err = networkAcls.Save(acls.ContainerID(ec.Network)); err != nil { 42 | slog.Error("failed to update network acls", "error", err) 43 | return err 44 | } 45 | return nil 46 | } 47 | SetClientACLs = func(ec *models.ExtClient, newACLs map[string]struct{}) { 48 | } 49 | UpdateProNodeACLs = func(node *models.Node) error { 50 | return nil 51 | } 52 | ) 53 | 54 | // SortExtClient - Sorts slice of ExtClients by their ClientID alphabetically with numbers first 55 | func SortExtClient(unsortedExtClient []models.ExtClient) { 56 | sort.Slice(unsortedExtClient, func(i, j int) bool { 57 | return unsortedExtClient[i].ClientID < unsortedExtClient[j].ClientID 58 | }) 59 | } 60 | 61 | // GetExtClientByName - gets an ext client by name 62 | func GetExtClientByName(ID string) (models.ExtClient, error) { 63 | clients, err := GetAllExtClients() 64 | if err != nil { 65 | return models.ExtClient{}, err 66 | } 67 | for i := range clients { 68 | if clients[i].ClientID == ID { 69 | return clients[i], nil 70 | } 71 | } 72 | return models.ExtClient{}, errors.New("client not found") 73 | } 74 | -------------------------------------------------------------------------------- /logic/errors.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/gravitl/netmaker/models" 8 | "golang.org/x/exp/slog" 9 | ) 10 | 11 | // FormatError - takes ErrorResponse and uses correct code 12 | func FormatError(err error, errType string) models.ErrorResponse { 13 | 14 | var status = http.StatusInternalServerError 15 | switch errType { 16 | case "internal": 17 | status = http.StatusInternalServerError 18 | case "badrequest": 19 | status = http.StatusBadRequest 20 | case "notfound": 21 | status = http.StatusNotFound 22 | case "unauthorized": 23 | status = http.StatusUnauthorized 24 | case "forbidden": 25 | status = http.StatusForbidden 26 | default: 27 | status = http.StatusInternalServerError 28 | } 29 | 30 | var response = models.ErrorResponse{ 31 | Message: err.Error(), 32 | Code: status, 33 | } 34 | return response 35 | } 36 | 37 | // ReturnSuccessResponse - processes message and adds header 38 | func ReturnSuccessResponse(response http.ResponseWriter, request *http.Request, message string) { 39 | var httpResponse models.SuccessResponse 40 | httpResponse.Code = http.StatusOK 41 | httpResponse.Message = message 42 | response.Header().Set("Content-Type", "application/json") 43 | response.WriteHeader(http.StatusOK) 44 | json.NewEncoder(response).Encode(httpResponse) 45 | } 46 | 47 | // ReturnSuccessResponseWithJson - processes message and adds header 48 | func ReturnSuccessResponseWithJson(response http.ResponseWriter, request *http.Request, res interface{}, message string) { 49 | var httpResponse models.SuccessResponse 50 | httpResponse.Code = http.StatusOK 51 | httpResponse.Response = res 52 | httpResponse.Message = message 53 | response.Header().Set("Content-Type", "application/json") 54 | response.WriteHeader(http.StatusOK) 55 | json.NewEncoder(response).Encode(httpResponse) 56 | } 57 | 58 | // ReturnErrorResponse - processes error and adds header 59 | func ReturnErrorResponse(response http.ResponseWriter, request *http.Request, errorMessage models.ErrorResponse) { 60 | httpResponse := &models.ErrorResponse{Code: errorMessage.Code, Message: errorMessage.Message} 61 | jsonResponse, err := json.Marshal(httpResponse) 62 | if err != nil { 63 | panic(err) 64 | } 65 | slog.Debug("processed request error", "err", errorMessage.Message) 66 | response.Header().Set("Content-Type", "application/json") 67 | response.WriteHeader(errorMessage.Code) 68 | response.Write(jsonResponse) 69 | } 70 | -------------------------------------------------------------------------------- /logic/host_test.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "os" 8 | "testing" 9 | 10 | "github.com/google/uuid" 11 | "github.com/gravitl/netmaker/database" 12 | "github.com/gravitl/netmaker/models" 13 | "github.com/matryer/is" 14 | ) 15 | 16 | func TestMain(m *testing.M) { 17 | database.InitializeDatabase() 18 | defer database.CloseDB() 19 | peerUpdate := make(chan *models.Node) 20 | go ManageZombies(context.Background(), peerUpdate) 21 | go func() { 22 | for y := range peerUpdate { 23 | fmt.Printf("Pointless %v\n", y) 24 | //do nothing 25 | } 26 | }() 27 | 28 | os.Exit(m.Run()) 29 | } 30 | 31 | func TestCheckPorts(t *testing.T) { 32 | h := models.Host{ 33 | ID: uuid.New(), 34 | EndpointIP: net.ParseIP("192.168.1.1"), 35 | ListenPort: 51821, 36 | } 37 | testHost := models.Host{ 38 | ID: uuid.New(), 39 | EndpointIP: net.ParseIP("192.168.1.1"), 40 | ListenPort: 51830, 41 | } 42 | //not sure why this initialization is required but without it 43 | // RemoveHost returns database is closed 44 | database.InitializeDatabase() 45 | RemoveHost(&h, true) 46 | CreateHost(&h) 47 | t.Run("no change", func(t *testing.T) { 48 | is := is.New(t) 49 | CheckHostPorts(&testHost) 50 | t.Log(testHost.ListenPort) 51 | t.Log(h.ListenPort) 52 | is.Equal(testHost.ListenPort, 51830) 53 | }) 54 | t.Run("same listen port", func(t *testing.T) { 55 | is := is.New(t) 56 | testHost.ListenPort = 51821 57 | CheckHostPorts(&testHost) 58 | t.Log(testHost.ListenPort) 59 | t.Log(h.ListenPort) 60 | is.Equal(testHost.ListenPort, 51822) 61 | }) 62 | 63 | } 64 | -------------------------------------------------------------------------------- /logic/hostactions/hostactions.go: -------------------------------------------------------------------------------- 1 | package hostactions 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/gravitl/netmaker/database" 7 | "github.com/gravitl/netmaker/models" 8 | ) 9 | 10 | // AddAction - adds a host action to a host's list to be retrieved from broker update 11 | func AddAction(hu models.HostUpdate) { 12 | hostID := hu.Host.ID.String() 13 | currentRecords, err := database.FetchRecord(database.HOST_ACTIONS_TABLE_NAME, hostID) 14 | if err != nil { 15 | if database.IsEmptyRecord(err) { // no list exists yet 16 | newEntry, err := json.Marshal([]models.HostUpdate{hu}) 17 | if err != nil { 18 | return 19 | } 20 | _ = database.Insert(hostID, string(newEntry), database.HOST_ACTIONS_TABLE_NAME) 21 | } 22 | return 23 | } 24 | var currentList []models.HostUpdate 25 | if err := json.Unmarshal([]byte(currentRecords), ¤tList); err != nil { 26 | return 27 | } 28 | currentList = append(currentList, hu) 29 | newData, err := json.Marshal(currentList) 30 | if err != nil { 31 | return 32 | } 33 | _ = database.Insert(hostID, string(newData), database.HOST_ACTIONS_TABLE_NAME) 34 | } 35 | 36 | // GetAction - gets an action if exists 37 | func GetAction(id string) *models.HostUpdate { 38 | currentRecords, err := database.FetchRecord(database.HOST_ACTIONS_TABLE_NAME, id) 39 | if err != nil { 40 | return nil 41 | } 42 | var currentList []models.HostUpdate 43 | if err = json.Unmarshal([]byte(currentRecords), ¤tList); err != nil { 44 | return nil 45 | } 46 | if len(currentList) > 0 { 47 | hu := currentList[0] 48 | newData, err := json.Marshal(currentList[1:]) 49 | if err != nil { 50 | newData, _ = json.Marshal([]models.HostUpdate{}) 51 | } 52 | _ = database.Insert(id, string(newData), database.HOST_ACTIONS_TABLE_NAME) 53 | return &hu 54 | } 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /logic/legacy.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/gravitl/netmaker/database" 7 | "github.com/gravitl/netmaker/logger" 8 | "github.com/gravitl/netmaker/models" 9 | ) 10 | 11 | // IsLegacyNode - checks if a node is legacy or not 12 | func IsLegacyNode(nodeID string) bool { 13 | record, err := database.FetchRecord(database.NODES_TABLE_NAME, nodeID) 14 | if err != nil { 15 | return false 16 | } 17 | var currentNode models.Node 18 | var legacyNode models.LegacyNode 19 | currentNodeErr := json.Unmarshal([]byte(record), ¤tNode) 20 | legacyNodeErr := json.Unmarshal([]byte(record), &legacyNode) 21 | return currentNodeErr != nil && legacyNodeErr == nil 22 | } 23 | 24 | // CheckAndRemoveLegacyNode - checks for legacy node and removes 25 | func CheckAndRemoveLegacyNode(nodeID string) bool { 26 | if IsLegacyNode(nodeID) { 27 | if err := database.DeleteRecord(database.NODES_TABLE_NAME, nodeID); err == nil { 28 | return true 29 | } 30 | } 31 | return false 32 | } 33 | 34 | // RemoveAllLegacyNodes - fetches all legacy nodes from DB and removes 35 | func RemoveAllLegacyNodes() error { 36 | records, err := database.FetchRecords(database.NODES_TABLE_NAME) 37 | if err != nil { 38 | return err 39 | } 40 | for k := range records { 41 | if CheckAndRemoveLegacyNode(k) { 42 | logger.Log(0, "removed legacy node", k) 43 | } 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /logic/metrics.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/models" 5 | ) 6 | 7 | var DeleteMetrics = func(string) error { 8 | return nil 9 | } 10 | 11 | var UpdateMetrics = func(string, *models.Metrics) error { 12 | return nil 13 | } 14 | 15 | var GetMetrics = func(string) (*models.Metrics, error) { 16 | var metrics models.Metrics 17 | return &metrics, nil 18 | } 19 | -------------------------------------------------------------------------------- /logic/nodes_test.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestContainsCIDR(t *testing.T) { 8 | 9 | b := ContainsCIDR("10.1.1.2/32", "10.1.1.0/24") 10 | if !b { 11 | t.Errorf("expected true, returned %v", b) 12 | } 13 | 14 | b = ContainsCIDR("10.1.1.2/32", "10.5.1.0/24") 15 | if b { 16 | t.Errorf("expected false, returned %v", b) 17 | } 18 | 19 | b = ContainsCIDR("fd52:65f5:d685:d11d::1/64", "fd52:65f5:d685:d11d::/64") 20 | if !b { 21 | t.Errorf("expected true, returned %v", b) 22 | } 23 | 24 | b1 := ContainsCIDR("fd10:10::/64", "fd10::/16") 25 | if !b1 { 26 | t.Errorf("expected true, returned %v", b1) 27 | } 28 | 29 | b1 = ContainsCIDR("fd10:10::/64", "fd10::/64") 30 | if b1 { 31 | t.Errorf("expected false, returned %v", b1) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /logic/pro/netcache/netcache.go: -------------------------------------------------------------------------------- 1 | package netcache 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/gravitl/netmaker/database" 9 | "github.com/gravitl/netmaker/models" 10 | ) 11 | 12 | const ( 13 | expirationTime = time.Minute * 5 14 | ) 15 | 16 | // CValue - the cache object for a network 17 | type CValue struct { 18 | Network string `json:"network,omitempty"` 19 | Value string `json:"value"` 20 | Host models.Host `json:"host"` 21 | Pass string `json:"pass,omitempty"` 22 | User string `json:"user,omitempty"` 23 | ALL bool `json:"all,omitempty"` 24 | Expiration time.Time `json:"expiration"` 25 | } 26 | 27 | var ErrExpired = fmt.Errorf("expired") 28 | 29 | // Set - sets a value to a key in db 30 | func Set(k string, newValue *CValue) error { 31 | newValue.Expiration = time.Now().Add(expirationTime) 32 | newData, err := json.Marshal(newValue) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | return database.Insert(k, string(newData), database.CACHE_TABLE_NAME) 38 | } 39 | 40 | // Get - gets a value from db, if expired, return err 41 | func Get(k string) (*CValue, error) { 42 | record, err := database.FetchRecord(database.CACHE_TABLE_NAME, k) 43 | if err != nil { 44 | return nil, err 45 | } 46 | var entry CValue 47 | if err := json.Unmarshal([]byte(record), &entry); err != nil { 48 | return nil, err 49 | } 50 | if time.Now().After(entry.Expiration) { 51 | return nil, ErrExpired 52 | } 53 | 54 | return &entry, nil 55 | } 56 | 57 | // Del - deletes a value from db 58 | func Del(k string) error { 59 | return database.DeleteRecord(database.CACHE_TABLE_NAME, k) 60 | } 61 | -------------------------------------------------------------------------------- /logic/proc.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | "runtime/pprof" 7 | 8 | "github.com/gravitl/netmaker/logger" 9 | ) 10 | 11 | func StartCPUProfiling() *os.File { 12 | f, err := os.OpenFile("/root/data/cpu.prof", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0755) 13 | if err != nil { 14 | logger.Log(0, "could not create CPU profile: ", err.Error()) 15 | } 16 | if err := pprof.StartCPUProfile(f); err != nil { 17 | logger.Log(0, "could not start CPU profile: ", err.Error()) 18 | } 19 | return f 20 | } 21 | 22 | func StopCPUProfiling(f *os.File) { 23 | pprof.StopCPUProfile() 24 | f.Close() 25 | } 26 | 27 | func StartMemProfiling() { 28 | f, err := os.OpenFile("/root/data/mem.prof", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0755) 29 | if err != nil { 30 | logger.Log(0, "could not create Memory profile: ", err.Error()) 31 | } 32 | defer f.Close() 33 | runtime.GC() // get up-to-date statistics 34 | if err = pprof.WriteHeapProfile(f); err != nil { 35 | logger.Log(0, "could not write memory profile: ", err.Error()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /logic/server.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | // EnterpriseCheckFuncs - can be set to run functions for EE 4 | var EnterpriseCheckFuncs []func() 5 | 6 | // == Join, Checkin, and Leave for Server == 7 | 8 | // KUBERNETES_LISTEN_PORT - starting port for Kubernetes in order to use NodePort range 9 | const KUBERNETES_LISTEN_PORT = 31821 10 | 11 | // KUBERNETES_SERVER_MTU - ideal mtu for kubernetes deployments right now 12 | const KUBERNETES_SERVER_MTU = 1024 13 | 14 | // EnterpriseCheck - Runs enterprise functions if presented 15 | func EnterpriseCheck() { 16 | for _, check := range EnterpriseCheckFuncs { 17 | check() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /logic/serverconf.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | 7 | "github.com/gravitl/netmaker/database" 8 | "github.com/gravitl/netmaker/servercfg" 9 | ) 10 | 11 | var ( 12 | // NetworksLimit - dummy var for community 13 | NetworksLimit = 1000000000 14 | // UsersLimit - dummy var for community 15 | UsersLimit = 1000000000 16 | // MachinesLimit - dummy var for community 17 | MachinesLimit = 1000000000 18 | // IngressesLimit - dummy var for community 19 | IngressesLimit = 1000000000 20 | // EgressesLimit - dummy var for community 21 | EgressesLimit = 1000000000 22 | // FreeTier - specifies if free tier 23 | FreeTier = false 24 | // DefaultTrialEndDate - is a placeholder date for not applicable trial end dates 25 | DefaultTrialEndDate, _ = time.Parse("2006-Jan-02", "2021-Apr-01") 26 | 27 | GetTrialEndDate = func() (time.Time, error) { 28 | return DefaultTrialEndDate, nil 29 | } 30 | ) 31 | 32 | type serverData struct { 33 | PrivateKey string `json:"privatekey,omitempty" bson:"privatekey,omitempty"` 34 | } 35 | 36 | // FetchJWTSecret - fetches jwt secret from db 37 | func FetchJWTSecret() (string, error) { 38 | var dbData string 39 | var err error 40 | var fetchedData = serverData{} 41 | dbData, err = database.FetchRecord(database.SERVERCONF_TABLE_NAME, "nm-jwt-secret") 42 | if err != nil { 43 | return "", err 44 | } 45 | err = json.Unmarshal([]byte(dbData), &fetchedData) 46 | if err != nil { 47 | return "", err 48 | } 49 | return fetchedData.PrivateKey, nil 50 | } 51 | 52 | // StoreJWTSecret - stores server jwt secret if needed 53 | func StoreJWTSecret(privateKey string) error { 54 | var newData = serverData{} 55 | var err error 56 | var data []byte 57 | newData.PrivateKey = privateKey 58 | data, err = json.Marshal(&newData) 59 | if err != nil { 60 | return err 61 | } 62 | return database.Insert("nm-jwt-secret", string(data), database.SERVERCONF_TABLE_NAME) 63 | } 64 | 65 | // SetFreeTierLimits - sets limits for free tier 66 | func SetFreeTierLimits() { 67 | FreeTier = true 68 | UsersLimit = servercfg.GetUserLimit() 69 | NetworksLimit = servercfg.GetNetworkLimit() 70 | MachinesLimit = servercfg.GetMachinesLimit() 71 | IngressesLimit = servercfg.GetIngressLimit() 72 | EgressesLimit = servercfg.GetEgressLimit() 73 | } 74 | -------------------------------------------------------------------------------- /logic/status.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/gravitl/netmaker/models" 7 | ) 8 | 9 | var GetNodeStatus = GetNodeCheckInStatus 10 | 11 | func GetNodeCheckInStatus(node *models.Node, t bool) { 12 | // On CE check only last check-in time 13 | if node.IsStatic { 14 | if !node.StaticNode.Enabled { 15 | node.Status = models.OfflineSt 16 | return 17 | } 18 | node.Status = models.OnlineSt 19 | return 20 | } 21 | if !node.Connected { 22 | node.Status = models.Disconnected 23 | return 24 | } 25 | if time.Since(node.LastCheckIn) > time.Minute*10 { 26 | node.Status = models.OfflineSt 27 | return 28 | } 29 | node.Status = models.OnlineSt 30 | } 31 | -------------------------------------------------------------------------------- /logic/traffic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | // RetrievePrivateTrafficKey - retrieves private key of server 4 | func RetrievePrivateTrafficKey() ([]byte, error) { 5 | var telRecord, err = FetchTelemetryRecord() 6 | if err != nil { 7 | return nil, err 8 | } 9 | 10 | return telRecord.TrafficKeyPriv, nil 11 | } 12 | 13 | // RetrievePublicTrafficKey - retrieves public key of server 14 | func RetrievePublicTrafficKey() ([]byte, error) { 15 | var telRecord, err = FetchTelemetryRecord() 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | return telRecord.TrafficKeyPub, nil 21 | } 22 | -------------------------------------------------------------------------------- /logic/util_test.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestRandomString(t *testing.T) { 11 | for testCase := 0; testCase < 100; testCase++ { 12 | for size := 2; size < 2058; size++ { 13 | if length := len(RandomString(size)); length != size { 14 | t.Fatalf("expected random string of size %d, got %d instead", size, length) 15 | } 16 | } 17 | } 18 | } 19 | 20 | func TestMakeRandomStringValid(t *testing.T) { 21 | lengthStr := RandomString(10) 22 | assert.Equal(t, len(lengthStr), 10) 23 | validMqID := RandomString(23) 24 | assert.False(t, strings.Contains(validMqID, "#")) 25 | assert.False(t, strings.Contains(validMqID, "!")) 26 | assert.False(t, strings.Contains(validMqID, "\"")) 27 | assert.False(t, strings.Contains(validMqID, "\\")) 28 | assert.False(t, strings.Contains(validMqID, "+")) 29 | assert.False(t, strings.Contains(validMqID, "-")) 30 | assert.False(t, strings.Contains(validMqID, "{")) 31 | assert.False(t, strings.Contains(validMqID, "}")) 32 | } 33 | -------------------------------------------------------------------------------- /logic/version.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "strings" 5 | "unicode" 6 | 7 | "github.com/hashicorp/go-version" 8 | ) 9 | 10 | const MinVersion = "v0.17.0" 11 | 12 | // IsVersionCompatible checks that the version passed is compabtible (>=) with MinVersion 13 | func IsVersionCompatible(ver string) bool { 14 | // during dev, assume developers know what they are doing 15 | if ver == "dev" { 16 | return true 17 | } 18 | trimmed := strings.TrimFunc(ver, func(r rune) bool { 19 | return !unicode.IsNumber(r) 20 | }) 21 | v, err := version.NewVersion(trimmed) 22 | if err != nil { 23 | return false 24 | } 25 | constraint, err := version.NewConstraint(">= " + MinVersion) 26 | if err != nil { 27 | return false 28 | } 29 | return constraint.Check(v) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /logic/version_test.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/matryer/is" 7 | ) 8 | 9 | func TestVersion(t *testing.T) { 10 | t.Run("valid version", func(t *testing.T) { 11 | is := is.New(t) 12 | valid := IsVersionCompatible("v0.17.1-testing") 13 | is.Equal(valid, true) 14 | }) 15 | t.Run("dev version", func(t *testing.T) { 16 | is := is.New(t) 17 | valid := IsVersionCompatible("dev") 18 | is.Equal(valid, true) 19 | }) 20 | t.Run("invalid version", func(t *testing.T) { 21 | is := is.New(t) 22 | valid := IsVersionCompatible("v0.14.2-refactor") 23 | is.Equal(valid, false) 24 | }) 25 | t.Run("no version", func(t *testing.T) { 26 | is := is.New(t) 27 | valid := IsVersionCompatible("testing") 28 | is.Equal(valid, false) 29 | }) 30 | t.Run("incomplete version", func(t *testing.T) { 31 | is := is.New(t) 32 | valid := IsVersionCompatible("0.18") 33 | is.Equal(valid, true) 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /logic/wireguard.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/models" 5 | ) 6 | 7 | // IfaceDelta - checks if the new node causes an interface change 8 | func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool { 9 | // single comparison statements 10 | if newNode.Address.String() != currentNode.Address.String() || 11 | newNode.Address6.String() != currentNode.Address6.String() || 12 | newNode.IsEgressGateway != currentNode.IsEgressGateway || 13 | newNode.IsIngressGateway != currentNode.IsIngressGateway || 14 | newNode.IsRelay != currentNode.IsRelay || 15 | newNode.DNSOn != currentNode.DNSOn || 16 | newNode.Connected != currentNode.Connected { 17 | return true 18 | } 19 | // multi-comparison statements 20 | if newNode.IsEgressGateway { 21 | if len(currentNode.EgressGatewayRanges) != len(newNode.EgressGatewayRanges) { 22 | return true 23 | } 24 | for _, address := range newNode.EgressGatewayRanges { 25 | if !StringSliceContains(currentNode.EgressGatewayRanges, address) { 26 | return true 27 | } 28 | } 29 | } 30 | if newNode.IsRelay { 31 | if len(currentNode.RelayedNodes) != len(newNode.RelayedNodes) { 32 | return true 33 | } 34 | for _, node := range newNode.RelayedNodes { 35 | if !StringSliceContains(currentNode.RelayedNodes, node) { 36 | return true 37 | } 38 | } 39 | } 40 | return false 41 | } 42 | 43 | // == Private Functions == 44 | -------------------------------------------------------------------------------- /main_ee.go: -------------------------------------------------------------------------------- 1 | //go:build ee 2 | // +build ee 3 | 4 | package main 5 | 6 | import ( 7 | "github.com/gravitl/netmaker/pro" 8 | _ "go.uber.org/automaxprocs" 9 | ) 10 | 11 | func init() { 12 | pro.InitPro() 13 | } 14 | -------------------------------------------------------------------------------- /models/accessToken.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // AccessToken - token used to access netmaker 4 | type AccessToken struct { 5 | APIConnString string `json:"apiconnstring"` 6 | ClientConfig 7 | } 8 | 9 | // ClientConfig - the config of the client 10 | type ClientConfig struct { 11 | Network string `json:"network"` 12 | Key string `json:"key"` 13 | } 14 | -------------------------------------------------------------------------------- /models/dnsEntry.go: -------------------------------------------------------------------------------- 1 | // TODO: Either add a returnNetwork and returnKey, or delete this 2 | package models 3 | 4 | // DNSUpdateAction identifies the action to be performed with the dns update data 5 | type DNSUpdateAction int 6 | 7 | const ( 8 | // DNSDeleteByIP delete the dns entry 9 | DNSDeleteByIP = iota 10 | // DNSDeleteByName delete the dns entry 11 | DNSDeleteByName 12 | // DNSReplaceName replace the dns entry 13 | DNSReplaceName 14 | // DNSReplaceIP resplace the dns entry 15 | DNSReplaceIP 16 | // DNSInsert insert a new dns entry 17 | DNSInsert 18 | ) 19 | 20 | func (action DNSUpdateAction) String() string { 21 | return [...]string{"DNSDeleteByIP", "DNSDeletByName", "DNSReplaceName", "DNSReplaceIP", "DNSInsert"}[action] 22 | } 23 | 24 | // DNSError.Error implementation of error interface 25 | func (e DNSError) Error() string { 26 | return "error publishing dns update" 27 | } 28 | 29 | // DNSError error struct capable of holding multiple error messages 30 | type DNSError struct { 31 | ErrorStrings []string 32 | } 33 | 34 | // DNSUpdate data for updating entries in /etc/hosts 35 | type DNSUpdate struct { 36 | Action DNSUpdateAction 37 | Name string 38 | NewName string 39 | Address string 40 | NewAddress string 41 | } 42 | 43 | // DNSEntry - a DNS entry represented as struct 44 | type DNSEntry struct { 45 | Address string `json:"address" validate:"omitempty,ip"` 46 | Address6 string `json:"address6" validate:"omitempty,ip"` 47 | Name string `json:"name" validate:"required,name_unique,min=1,max=192,whitespace"` 48 | Network string `json:"network" validate:"network_exists"` 49 | } 50 | -------------------------------------------------------------------------------- /models/error.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Error string 4 | 5 | func (e Error) Error() string { return string(e) } 6 | -------------------------------------------------------------------------------- /models/gateway.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type CreateGwReq struct { 4 | IngressRequest 5 | RelayRequest 6 | } 7 | 8 | type DeleteGw struct { 9 | } 10 | -------------------------------------------------------------------------------- /models/intclient.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type IntClient struct { 4 | ClientID string `json:"clientid" bson:"clientid"` 5 | PrivateKey string `json:"privatekey" bson:"privatekey"` 6 | PublicKey string `json:"publickey" bson:"publickey"` 7 | AccessKey string `json:"accesskey" bson:"accesskey"` 8 | Address string `json:"address" bson:"address"` 9 | Address6 string `json:"address6" bson:"address6"` 10 | Network string `json:"network" bson:"network"` 11 | ServerPublicEndpoint string `json:"serverpublicendpoint" bson:"serverpublicendpoint"` 12 | ServerAPIPort string `json:"serverapiport" bson:"serverapiport"` 13 | ServerPrivateAddress string `json:"serverprivateaddress" bson:"serverprivateaddress"` 14 | ServerWGPort string `json:"serverwgport" bson:"serverwgport"` 15 | ServerKey string `json:"serverkey" bson:"serverkey"` 16 | IsServer string `json:"isserver" bson:"isserver"` 17 | } 18 | -------------------------------------------------------------------------------- /models/migrate.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // MigrationData struct needed to create new v0.18.0 node from v.0.17.X node 4 | type MigrationData struct { 5 | HostName string 6 | Password string 7 | OS string 8 | LegacyNodes []LegacyNode 9 | } 10 | -------------------------------------------------------------------------------- /models/names.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/goombaio/namegenerator" 7 | ) 8 | 9 | var logoString = retrieveLogo() 10 | 11 | // GenerateNodeName - generates a random node name 12 | func GenerateNodeName() string { 13 | seed := time.Now().UTC().UnixNano() 14 | nameGenerator := namegenerator.NewNameGenerator(seed) 15 | return nameGenerator.Generate() 16 | } 17 | 18 | // RetrieveLogo - retrieves the ascii art logo for Netmaker 19 | func RetrieveLogo() string { 20 | return logoString 21 | } 22 | 23 | // SetLogo - sets the logo ascii art 24 | func SetLogo(logo string) { 25 | logoString = logo 26 | } 27 | 28 | func retrieveLogo() string { 29 | return ` 30 | __ __ ______ ______ __ __ ______ __ __ ______ ______ 31 | /\ "-.\ \ /\ ___\ /\__ _\ /\ "-./ \ /\ __ \ /\ \/ / /\ ___\ /\ == \ 32 | \ \ \-. \ \ \ __\ \/_/\ \/ \ \ \-./\ \ \ \ __ \ \ \ _"-. \ \ __\ \ \ __< 33 | \ \_\\"\_\ \ \_____\ \ \_\ \ \_\ \ \_\ \ \_\ \_\ \ \_\ \_\ \ \_____\ \ \_\ \_\ 34 | \/_/ \/_/ \/_____/ \/_/ \/_/ \/_/ \/_/\/_/ \/_/\/_/ \/_____/ \/_/ /_/ 35 | 36 | ` 37 | } 38 | -------------------------------------------------------------------------------- /models/network_test.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // moved from controllers need work 4 | //func TestUpdateNetwork(t *testing.T) { 5 | // initialize() 6 | // createNet() 7 | // network := getNet() 8 | // t.Run("NetID", func(t *testing.T) { 9 | // var networkupdate models.Network 10 | // networkupdate.NetID = "wirecat" 11 | // Range, local, err := network.Update(&networkupdate) 12 | // assert.NotNil(t, err) 13 | // assert.Equal(t, "NetID is not editable", err.Error()) 14 | // t.Log(err, Range, local) 15 | // }) 16 | //} 17 | -------------------------------------------------------------------------------- /models/ssocache.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | // DefaultExpDuration - the default expiration time of SsoState 6 | const DefaultExpDuration = time.Minute * 5 7 | 8 | // SsoState - holds SSO sign-in session data 9 | type SsoState struct { 10 | Value string `json:"value"` 11 | Expiration time.Time `json:"expiration"` 12 | } 13 | 14 | // SsoState.IsExpired - tells if an SsoState is expired or not 15 | func (s *SsoState) IsExpired() bool { 16 | return time.Now().After(s.Expiration) 17 | } 18 | -------------------------------------------------------------------------------- /models/tags.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type TagID string 9 | 10 | const ( 11 | OldRemoteAccessTagName = "remote-access-gws" 12 | GwTagName = "gateways" 13 | ) 14 | 15 | func (id TagID) String() string { 16 | return string(id) 17 | } 18 | 19 | func (t Tag) GetIDFromName() string { 20 | return fmt.Sprintf("%s.%s", t.Network, t.TagName) 21 | } 22 | 23 | type Tag struct { 24 | ID TagID `json:"id"` 25 | TagName string `json:"tag_name"` 26 | Network NetworkID `json:"network"` 27 | ColorCode string `json:"color_code"` 28 | CreatedBy string `json:"created_by"` 29 | CreatedAt time.Time `json:"created_at"` 30 | } 31 | 32 | type CreateTagReq struct { 33 | TagName string `json:"tag_name"` 34 | Network NetworkID `json:"network"` 35 | ColorCode string `json:"color_code"` 36 | TaggedNodes []ApiNode `json:"tagged_nodes"` 37 | } 38 | 39 | type TagListResp struct { 40 | Tag 41 | UsedByCnt int `json:"used_by_count"` 42 | TaggedNodes []ApiNode `json:"tagged_nodes"` 43 | } 44 | 45 | type TagListRespNodes struct { 46 | Tag 47 | UsedByCnt int `json:"used_by_count"` 48 | TaggedNodes []ApiNode `json:"tagged_nodes"` 49 | } 50 | 51 | type UpdateTagReq struct { 52 | Tag 53 | NewName string `json:"new_name"` 54 | ColorCode string `json:"color_code"` 55 | TaggedNodes []ApiNode `json:"tagged_nodes"` 56 | } 57 | -------------------------------------------------------------------------------- /mq/emqx.go: -------------------------------------------------------------------------------- 1 | package mq 2 | 3 | import "github.com/gravitl/netmaker/servercfg" 4 | 5 | var emqx Emqx 6 | 7 | type Emqx interface { 8 | GetType() servercfg.Emqxdeploy 9 | CreateEmqxUser(username, password string) error 10 | CreateEmqxUserforServer() error 11 | CreateEmqxDefaultAuthenticator() error 12 | CreateEmqxDefaultAuthorizer() error 13 | CreateDefaultAllowRule() error 14 | DeleteEmqxUser(username string) error 15 | } 16 | 17 | func init() { 18 | if servercfg.GetBrokerType() != servercfg.EmqxBrokerType { 19 | return 20 | } 21 | if servercfg.GetEmqxDeployType() == servercfg.EmqxCloudDeploy { 22 | emqx = &EmqxCloud{ 23 | URL: servercfg.GetEmqxRestEndpoint(), 24 | AppID: servercfg.GetEmqxAppID(), 25 | AppSecret: servercfg.GetEmqxAppSecret(), 26 | } 27 | } else { 28 | emqx = &EmqxOnPrem{ 29 | URL: servercfg.GetEmqxRestEndpoint(), 30 | UserName: servercfg.GetMqUserName(), 31 | Password: servercfg.GetMqPassword(), 32 | } 33 | } 34 | } 35 | 36 | // GetEmqxHandler - gets emqx handler 37 | func GetEmqxHandler() Emqx { 38 | return emqx 39 | } 40 | -------------------------------------------------------------------------------- /netclient/ncutils/constants.go: -------------------------------------------------------------------------------- 1 | package ncutils 2 | 3 | const ( 4 | // ACK - acknowledgement signal for MQ 5 | ACK = 1 6 | // DONE - done signal for MQ 7 | DONE = 2 8 | ) 9 | -------------------------------------------------------------------------------- /netclient/ncutils/iface.go: -------------------------------------------------------------------------------- 1 | package ncutils 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // StringSliceContains - sees if a string slice contains a string element 8 | func StringSliceContains(slice []string, item string) bool { 9 | for _, s := range slice { 10 | if s == item { 11 | return true 12 | } 13 | } 14 | return false 15 | } 16 | 17 | func IpIsPrivate(ipnet net.IP) bool { 18 | return ipnet.IsPrivate() || ipnet.IsLoopback() 19 | } 20 | -------------------------------------------------------------------------------- /netclient/ncutils/netclientutils.go: -------------------------------------------------------------------------------- 1 | package ncutils 2 | 3 | import ( 4 | "bytes" 5 | "encoding/gob" 6 | ) 7 | 8 | // DEFAULT_GC_PERCENT - garbage collection percent 9 | const DEFAULT_GC_PERCENT = 100 10 | 11 | // == OS PATH FUNCTIONS == 12 | 13 | // ConvertKeyToBytes - util to convert a key to bytes to use elsewhere 14 | func ConvertKeyToBytes(key *[32]byte) ([]byte, error) { 15 | var buffer bytes.Buffer 16 | var enc = gob.NewEncoder(&buffer) 17 | if err := enc.Encode(key); err != nil { 18 | return nil, err 19 | } 20 | return buffer.Bytes(), nil 21 | } 22 | 23 | // ConvertBytesToKey - util to convert bytes to a key to use elsewhere 24 | func ConvertBytesToKey(data []byte) (*[32]byte, error) { 25 | var buffer = bytes.NewBuffer(data) 26 | var dec = gob.NewDecoder(buffer) 27 | var result = new([32]byte) 28 | var err = dec.Decode(result) 29 | if err != nil { 30 | return nil, err 31 | } 32 | return result, err 33 | } 34 | -------------------------------------------------------------------------------- /netclient/ncutils/util.go: -------------------------------------------------------------------------------- 1 | package ncutils 2 | 3 | // CheckInInterval - the interval for check-in time in units/minute 4 | const CheckInInterval = 1 5 | -------------------------------------------------------------------------------- /nginx/netmaker-nginx-template.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name *.NETMAKER_BASE_DOMAIN; 4 | return 301 https://$host$request_uri; 5 | } 6 | 7 | server { 8 | listen 443 ssl; 9 | listen [::]:443 ssl; 10 | server_name dashboard.NETMAKER_BASE_DOMAIN; 11 | ssl_certificate /etc/letsencrypt/live/NETMAKER_BASE_DOMAIN/fullchain.pem; 12 | ssl_certificate_key /etc/letsencrypt/live/NETMAKER_BASE_DOMAIN/privkey.pem; 13 | #include /etc/letsencrypt/options-ssl-nginx.conf; 14 | #ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; 15 | location / { 16 | proxy_pass http://127.0.0.1:8082; 17 | } 18 | } 19 | server { 20 | listen 443 ssl; 21 | listen [::]:443 ssl; 22 | server_name api.NETMAKER_BASE_DOMAIN; 23 | ssl_certificate /etc/letsencrypt/live/NETMAKER_BASE_DOMAIN/fullchain.pem; 24 | ssl_certificate_key /etc/letsencrypt/live/NETMAKER_BASE_DOMAIN/privkey.pem; 25 | #include /etc/letsencrypt/options-ssl-nginx.conf; 26 | #ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; 27 | 28 | location / { 29 | proxy_pass http://127.0.0.1:8081; 30 | proxy_set_header Host api.NETMAKER_BASE_DOMAIN; 31 | proxy_pass_request_headers on; 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /pro/LICENSE: -------------------------------------------------------------------------------- 1 | The Netmaker Enterprise license (the “Enterprise License”) 2 | Copyright (c) 2022 Netmaker, Inc. 3 | 4 | With regard to the Netmaker Software: 5 | 6 | This software and associated documentation files (the "Software") may only be used in production, if you (and any entity that you represent) have agreed to, and are in compliance with, the Netmaker Subscription Terms of Service, available at https://www.netmaker.io/terms-and-conditions (the “Enterprise Terms”), or other agreement governing the use of the Software, as agreed by you and Netmaker, and otherwise have a valid Netmaker Enterprise license for the correct number of users, networks, nodes, servers, and external clients. Subject to the foregoing sentence, you are free to modify this Software and publish patches to the Software. You agree that Netmaker and/or its licensors (as applicable) retain all right, title and interest in and to all such modifications and/or patches, and all such modifications and/or patches may only be used, copied, modified, displayed, distributed, or otherwise exploited with a valid Netmaker Enterprise license for the correct number of users, networks, nodes, servers, and external clients as allocated by the license. Notwithstanding the foregoing, you may copy and modify the Software for development and testing purposes, without requiring a subscription. You agree that Netmaker and/or its licensors (as applicable) retain all right, title and interest in and to all such modifications. You are not granted any other rights beyond what is expressly stated herein. Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense, and/or sell the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | For all third party components incorporated into the Netmaker Software, those components are licensed under the original license provided by the owner of the applicable component. 11 | -------------------------------------------------------------------------------- /pro/controllers/middleware.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gravitl/netmaker/logic" 7 | "github.com/gravitl/netmaker/servercfg" 8 | ) 9 | 10 | var limitedApis = map[string]struct{}{ 11 | "/api/server/status": {}, 12 | "/api/emqx/hosts": {}, 13 | "/api/users/adm/authenticate": {}, 14 | } 15 | 16 | func OnlyServerAPIWhenUnlicensedMiddleware(handler http.Handler) http.Handler { 17 | return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { 18 | if servercfg.ErrLicenseValidation != nil { 19 | if _, ok := limitedApis[request.URL.Path]; !ok { 20 | logic.ReturnErrorResponse(writer, request, logic.FormatError(servercfg.ErrLicenseValidation, "forbidden")) 21 | return 22 | } 23 | } 24 | handler.ServeHTTP(writer, request) 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /pro/controllers/rac.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gorilla/mux" 7 | "github.com/gravitl/netmaker/logic" 8 | ) 9 | 10 | func RacHandlers(r *mux.Router) { 11 | r.HandleFunc("/api/v1/rac/networks", logic.SecurityCheck(false, http.HandlerFunc(getUserRemoteAccessNetworks))).Methods(http.MethodGet) 12 | r.HandleFunc("/api/v1/rac/network/{network}/access_points", logic.SecurityCheck(false, http.HandlerFunc(getUserRemoteAccessNetworkGateways))).Methods(http.MethodGet) 13 | r.HandleFunc("/api/v1/rac/access_point/{access_point_id}/config", logic.SecurityCheck(false, http.HandlerFunc(getRemoteAccessGatewayConf))).Methods(http.MethodGet) 14 | } 15 | -------------------------------------------------------------------------------- /pro/email/email.go: -------------------------------------------------------------------------------- 1 | package email 2 | 3 | import ( 4 | "context" 5 | "regexp" 6 | 7 | "github.com/gravitl/netmaker/servercfg" 8 | ) 9 | 10 | type EmailSenderType string 11 | 12 | var client EmailSender 13 | 14 | const ( 15 | Smtp EmailSenderType = "smtp" 16 | Resend EmailSenderType = "resend" 17 | ) 18 | 19 | func init() { 20 | 21 | smtpSender := &SmtpSender{ 22 | SmtpHost: servercfg.GetSmtpHost(), 23 | SmtpPort: servercfg.GetSmtpPort(), 24 | SenderEmail: servercfg.GetSenderEmail(), 25 | SendUser: servercfg.GetSenderUser(), 26 | SenderPass: servercfg.GetEmaiSenderPassword(), 27 | } 28 | if smtpSender.SendUser == "" { 29 | smtpSender.SendUser = smtpSender.SenderEmail 30 | } 31 | client = smtpSender 32 | 33 | } 34 | 35 | // EmailSender - an interface for sending emails based on notifications and mail templates 36 | type EmailSender interface { 37 | // SendEmail - sends an email based on a context, notification and mail template 38 | SendEmail(ctx context.Context, notification Notification, email Mail) error 39 | } 40 | 41 | type Mail interface { 42 | GetBody(info Notification) string 43 | GetSubject(info Notification) string 44 | } 45 | 46 | // Notification - struct for notification details 47 | type Notification struct { 48 | RecipientMail string 49 | RecipientName string 50 | ProductName string 51 | } 52 | 53 | func GetClient() (e EmailSender) { 54 | return client 55 | } 56 | 57 | func IsValid(email string) bool { 58 | emailRegex := regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`) 59 | return emailRegex.MatchString(email) 60 | } 61 | -------------------------------------------------------------------------------- /pro/email/smtp.go: -------------------------------------------------------------------------------- 1 | package email 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | 7 | gomail "gopkg.in/mail.v2" 8 | ) 9 | 10 | type SmtpSender struct { 11 | SmtpHost string 12 | SmtpPort int 13 | SenderEmail string 14 | SendUser string 15 | SenderPass string 16 | } 17 | 18 | func (s *SmtpSender) SendEmail(ctx context.Context, n Notification, e Mail) error { 19 | m := gomail.NewMessage() 20 | 21 | // Set E-Mail sender 22 | m.SetHeader("From", s.SenderEmail) 23 | 24 | // Set E-Mail receivers 25 | m.SetHeader("To", n.RecipientMail) 26 | // Set E-Mail subject 27 | m.SetHeader("Subject", e.GetSubject(n)) 28 | // Set E-Mail body. You can set plain text or html with text/html 29 | m.SetBody("text/html", e.GetBody(n)) 30 | // Settings for SMTP server 31 | d := gomail.NewDialer(s.SmtpHost, s.SmtpPort, s.SendUser, s.SenderPass) 32 | 33 | // This is only needed when SSL/TLS certificate is not valid on server. 34 | // In production this should be set to false. 35 | d.TLSConfig = &tls.Config{InsecureSkipVerify: true} 36 | 37 | // Now send E-Mail 38 | if err := d.DialAndSend(m); err != nil { 39 | return err 40 | } 41 | 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /pro/license_test.go: -------------------------------------------------------------------------------- 1 | //go:build ee 2 | // +build ee 3 | 4 | package pro 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/gravitl/netmaker/config" 10 | proLogic "github.com/gravitl/netmaker/pro/logic" 11 | ) 12 | 13 | func Test_GetAccountsHost(t *testing.T) { 14 | tests := []struct { 15 | name string 16 | envK string 17 | envV string 18 | conf string 19 | want string 20 | }{ 21 | { 22 | name: "no env var and no conf", 23 | envK: "NOT_THE_CORRECT_ENV_VAR", 24 | envV: "dev", 25 | want: "https://api.accounts.netmaker.io", 26 | }, 27 | { 28 | name: "dev env var", 29 | envK: "ENVIRONMENT", 30 | envV: "dev", 31 | want: "https://api.dev.accounts.netmaker.io", 32 | }, 33 | { 34 | name: "staging env var", 35 | envK: "ENVIRONMENT", 36 | envV: "staging", 37 | want: "https://api.staging.accounts.netmaker.io", 38 | }, 39 | { 40 | name: "prod env var", 41 | envK: "ENVIRONMENT", 42 | envV: "prod", 43 | want: "https://api.accounts.netmaker.io", 44 | }, 45 | { 46 | name: "dev conf", 47 | conf: "dev", 48 | want: "https://api.dev.accounts.netmaker.io", 49 | }, 50 | { 51 | name: "staging conf", 52 | conf: "staging", 53 | want: "https://api.staging.accounts.netmaker.io", 54 | }, 55 | { 56 | name: "prod conf", 57 | conf: "prod", 58 | want: "https://api.accounts.netmaker.io", 59 | }, 60 | { 61 | name: "env var vs conf precedence", 62 | envK: "ENVIRONMENT", 63 | envV: "prod", 64 | conf: "staging", 65 | want: "https://api.accounts.netmaker.io", 66 | }, 67 | } 68 | for _, tt := range tests { 69 | t.Run(tt.name, func(t *testing.T) { 70 | config.Config.Server.Environment = tt.conf 71 | if tt.envK != "" { 72 | t.Setenv(tt.envK, tt.envV) 73 | } 74 | if got := proLogic.GetAccountsHost(); got != tt.want { 75 | t.Errorf("GetAccountsHost() = %v, want %v", got, tt.want) 76 | } 77 | }) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /pro/logic/migrate.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gravitl/netmaker/logic" 7 | "github.com/gravitl/netmaker/models" 8 | ) 9 | 10 | func MigrateUserRoleAndGroups(user models.User) { 11 | var err error 12 | if user.PlatformRoleID == models.AdminRole || user.PlatformRoleID == models.SuperAdminRole { 13 | return 14 | } 15 | if len(user.RemoteGwIDs) > 0 { 16 | // define user roles for network 17 | // assign relevant network role to user 18 | for remoteGwID := range user.RemoteGwIDs { 19 | gwNode, err := logic.GetNodeByID(remoteGwID) 20 | if err != nil { 21 | continue 22 | } 23 | var g models.UserGroup 24 | if user.PlatformRoleID == models.ServiceUser { 25 | g, err = GetUserGroup(models.UserGroupID(fmt.Sprintf("%s-%s-grp", gwNode.Network, models.NetworkUser))) 26 | } else { 27 | g, err = GetUserGroup(models.UserGroupID(fmt.Sprintf("%s-%s-grp", 28 | gwNode.Network, models.NetworkAdmin))) 29 | } 30 | if err != nil { 31 | continue 32 | } 33 | user.UserGroups[g.ID] = struct{}{} 34 | } 35 | } 36 | if len(user.NetworkRoles) > 0 { 37 | for netID, netRoles := range user.NetworkRoles { 38 | var g models.UserGroup 39 | adminAccess := false 40 | for netRoleID := range netRoles { 41 | permTemplate, err := logic.GetRole(netRoleID) 42 | if err == nil { 43 | if permTemplate.FullAccess { 44 | adminAccess = true 45 | } 46 | } 47 | } 48 | 49 | if user.PlatformRoleID == models.ServiceUser { 50 | g, err = GetUserGroup(models.UserGroupID(fmt.Sprintf("%s-%s-grp", netID, models.NetworkUser))) 51 | } else { 52 | role := models.NetworkUser 53 | if adminAccess { 54 | role = models.NetworkAdmin 55 | } 56 | g, err = GetUserGroup(models.UserGroupID(fmt.Sprintf("%s-%s-grp", 57 | netID, role))) 58 | } 59 | if err != nil { 60 | continue 61 | } 62 | user.UserGroups[g.ID] = struct{}{} 63 | user.NetworkRoles = make(map[models.NetworkID]map[models.UserRoleID]struct{}) 64 | } 65 | 66 | } 67 | logic.UpsertUser(user) 68 | } 69 | -------------------------------------------------------------------------------- /pro/util.go: -------------------------------------------------------------------------------- 1 | //go:build ee 2 | // +build ee 3 | 4 | package pro 5 | 6 | import ( 7 | "encoding/base64" 8 | "github.com/gravitl/netmaker/models" 9 | 10 | "github.com/gravitl/netmaker/logic" 11 | ) 12 | 13 | // base64encode - base64 encode helper function 14 | func base64encode(input []byte) string { 15 | return base64.StdEncoding.EncodeToString(input) 16 | } 17 | 18 | // base64decode - base64 decode helper function 19 | func base64decode(input string) []byte { 20 | bytes, err := base64.StdEncoding.DecodeString(input) 21 | if err != nil { 22 | return nil 23 | } 24 | 25 | return bytes 26 | } 27 | 28 | func getCurrentServerUsage() (limits Usage) { 29 | limits.SetDefaults() 30 | hosts, hErr := logic.GetAllHostsWithStatus(models.OnlineSt) 31 | if hErr == nil { 32 | limits.Hosts = len(hosts) 33 | } 34 | clients, cErr := logic.GetAllExtClientsWithStatus(models.OnlineSt) 35 | if cErr == nil { 36 | limits.Clients = len(clients) 37 | } 38 | users, err := logic.GetUsers() 39 | if err == nil { 40 | limits.Users = len(users) 41 | } 42 | networks, err := logic.GetNetworks() 43 | if err == nil { 44 | limits.Networks = len(networks) 45 | } 46 | // TODO this part bellow can be optimized to get nodes just once 47 | ingresses, err := logic.GetAllIngresses() 48 | if err == nil { 49 | limits.Ingresses = len(ingresses) 50 | } 51 | egresses, err := logic.GetAllEgresses() 52 | if err == nil { 53 | limits.Egresses = len(egresses) 54 | } 55 | relays, err := logic.GetRelays() 56 | if err == nil { 57 | limits.Relays = len(relays) 58 | } 59 | gateways, err := logic.GetInternetGateways() 60 | if err == nil { 61 | limits.InternetGateways = len(gateways) 62 | } 63 | failovers, err := logic.GetAllFailOvers() 64 | if err == nil { 65 | limits.FailOvers = len(failovers) 66 | } 67 | return 68 | } 69 | -------------------------------------------------------------------------------- /release.md: -------------------------------------------------------------------------------- 1 | # Netmaker v0.90.0 2 | 3 | ## Whats New ✨ 4 | - ACL Rules for Egress Ranges 5 | - High Availability for Egress Routes 6 | - Remote Access Gateways and Relays have been merged together into "Gateways" and are now available on CE. 7 | - Enchanced Graph Page 8 | - Ability to Define Additional Nameservers in your network 9 | 10 | ## What's Fixed/Improved 🛠 11 | - Metrics Data 12 | - IPv6 DNS Entries Are Not Working. 13 | - FailOver connection improvements. 14 | - Optimized Failover peer signaling. 15 | - Improved Connectivity Status Indicator with real-time troubleshooting help. 16 | 17 | ## Known Issues 🐞 18 | 19 | - WireGuard DNS issue on Ubuntu 24.04 and some other newer Linux distributions. The issue is affecting the Remote Access Client (RAC) and the plain WireGuard external clients. Workaround can be found here https://help.netmaker.io/en/articles/9612016-extclient-rac-dns-issue-on-ubuntu-24-04. 20 | 21 | -------------------------------------------------------------------------------- /scripts/build-binaries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #server build 4 | env CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags="-X 'main.version=$VERSION'" -o netclient/build/netmaker main.go 5 | 6 | cd netclient 7 | ./bin-maker.sh 8 | 9 | -------------------------------------------------------------------------------- /scripts/netclient-rc-freebsd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # PROVIDE: netclient 4 | # REQUIRE: LOGIN DAEMON NETWORKING SERVERS FILESYSTEM 5 | # BEFORE: 6 | # KEYWORD: shutdown 7 | 8 | . /etc/rc.subr 9 | 10 | name="netclient" 11 | rcvar=netclient_enable 12 | pidfile="/var/run/${name}.pid" 13 | command="/sbin/daemon" 14 | command_args="-c -f -P ${pidfile} -R 10 -t "Netclient" -u root -o /etc/netclient/netclient.log /etc/netclient/netclient checkin -n all" 15 | 16 | load_rc_config $name 17 | run_rc_command "$1" 18 | -------------------------------------------------------------------------------- /scripts/netclient.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Define cleanup 4 | cleanup() { 5 | nets=($(wg show interfaces)) 6 | for net in ${nets[@]}; do 7 | echo "deleting interface" $net 8 | ip link del $net 9 | done 10 | } 11 | 12 | #Trap SigTerm 13 | trap 'cleanup' SIGTERM 14 | 15 | echo "[netclient] joining network" 16 | 17 | if [ -z "${SLEEP}" ]; then 18 | SLEEP=10 19 | fi 20 | 21 | TOKEN_CMD="" 22 | if [ "$TOKEN" != "" ]; then 23 | TOKEN_CMD="-t $TOKEN" 24 | fi 25 | 26 | /root/netclient join $TOKEN_CMD -udpholepunch no 27 | if [ $? -ne 0 ]; then { echo "Failed to join, quitting." ; exit 1; } fi 28 | 29 | echo "[netclient] Starting netclient daemon" 30 | 31 | /root/netclient daemon & 32 | 33 | wait $! 34 | echo "[netclient] exiting" 35 | -------------------------------------------------------------------------------- /scripts/openwrt-daemon-2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | #Created by oycol 3 | 4 | EXTRA_COMMANDS="status" 5 | EXTRA_HELP=" status Check service is running" 6 | START=99 7 | 8 | LOG_FILE="/tmp/netclient.logs" 9 | 10 | start() { 11 | if [ ! -f "${LOG_FILE}" ];then 12 | touch "${LOG_FILE}" 13 | fi 14 | local PID=$(ps -e|grep "netclient checkin -n all"|grep -v grep|awk '{print $1}') 15 | if [ "${PID}" ];then 16 | echo "service is running" 17 | return 18 | fi 19 | bash -c "while [ 1 ]; do /etc/netclient/netclient checkin -n all >> ${LOG_FILE} 2>&1;sleep 15;\ 20 | if [ $(ls -l ${LOG_FILE}|awk '{print $5}') -gt 10240000 ];then tar zcf "${LOG_FILE}.tar" -C / "tmp/netclient.logs" && > $LOG_FILE;fi;done &" 21 | echo "start" 22 | } 23 | 24 | stop() { 25 | local PID=$(ps -e|grep "netclient checkin -n all"|grep -v grep|awk '{print $1}') 26 | if [ "${PID}" ];then 27 | kill "${PID}" 28 | fi 29 | echo "stop" 30 | } 31 | 32 | status() { 33 | local PID=$(ps -e|grep "netclient checkin -n all"|grep -v grep|awk '{print $1}') 34 | if [ "${PID}" ];then 35 | echo -e "netclient[${PID}] is running \n" 36 | else 37 | echo -e "netclient is not running \n" 38 | fi 39 | } -------------------------------------------------------------------------------- /scripts/openwrt-daemon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | #Created by oycol 3 | 4 | EXTRA_COMMANDS="status" 5 | EXTRA_HELP=" status Check service is running" 6 | START=99 7 | 8 | LOG_FILE="/tmp/netclient.logs" 9 | 10 | start() { 11 | mkdir -p /etc/netclient/config 12 | mkdir -p /etc/systemd/system 13 | 14 | if [ ! -f "${LOG_FILE}" ];then 15 | touch "${LOG_FILE}" 16 | fi 17 | 18 | local PID=$(ps|grep "netclient daemon"|grep -v grep|awk '{print $1}') 19 | 20 | if [ "${PID}" ];then 21 | echo "service is running" 22 | return 23 | fi 24 | /bin/sh -c "while [ 1 ]; do netclient daemon >> ${LOG_FILE} 2>&1;sleep 15;\ 25 | if [ $(ls -l ${LOG_FILE}|awk '{print $5}') -gt 10240000 ];then tar zcf "${LOG_FILE}.tar" -C / "tmp/netclient.logs" && > $LOG_FILE;fi;done &" 26 | echo "start" 27 | } 28 | 29 | stop() { 30 | local PID=$(ps|grep "netclient daemon"|grep -v grep|awk '{print $1}') 31 | if [ "${PID}" ];then 32 | kill ${PID} 33 | fi 34 | echo "stop" 35 | } 36 | 37 | status() { 38 | local PID=$(ps|grep "netclient daemon"|grep -v grep|awk '{print $1}') 39 | if [ "${PID}" ];then 40 | echo -e "netclient[${PID}] is running \n" 41 | else 42 | echo -e "netclient is not running \n" 43 | fi 44 | } 45 | -------------------------------------------------------------------------------- /scripts/token-convert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | token=$1 6 | 7 | token_json=$(echo $token | base64 -d) 8 | 9 | api_addr=$(echo $token_json | jq -r '.apiconnstring') 10 | network=$(echo $token_json | jq -r '.network') 11 | key=$(echo $token_json | jq -r '.key') 12 | 13 | echo ./netclient join -k $key -n $network --apiserver $api_addr 14 | -------------------------------------------------------------------------------- /scripts/userspace-entrypoint.sh: -------------------------------------------------------------------------------- 1 | # If running userspace wireguard in Docker, create missing tun device. 2 | if [ ! -d /dev/net ]; then mkdir /dev/net; fi 3 | if [ ! -e /dev/net/tun ]; then mknod /dev/net/tun c 10 200; fi 4 | 5 | # Wait and then run netmaker. 6 | /bin/sh -c "sleep 3; ./netmaker" -------------------------------------------------------------------------------- /servercfg/serverconf_test.go: -------------------------------------------------------------------------------- 1 | package servercfg 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/matryer/is" 7 | ) 8 | 9 | func TestValidateDomain(t *testing.T) { 10 | 11 | t.Run("", func(t *testing.T) { 12 | is := is.New(t) 13 | valid := validateDomain("netmaker.hosted") 14 | is.Equal(valid, true) 15 | }) 16 | 17 | t.Run("", func(t *testing.T) { 18 | is := is.New(t) 19 | valid := validateDomain("ipv4test1.hosted") 20 | is.Equal(valid, true) 21 | }) 22 | 23 | t.Run("", func(t *testing.T) { 24 | is := is.New(t) 25 | valid := validateDomain("ip_4?") 26 | is.Equal(valid, false) 27 | }) 28 | 29 | } 30 | -------------------------------------------------------------------------------- /servercfg/sqlconf.go: -------------------------------------------------------------------------------- 1 | package servercfg 2 | 3 | import ( 4 | "github.com/gravitl/netmaker/config" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | func GetSQLConf() config.SQLConfig { 10 | var cfg config.SQLConfig 11 | cfg.Host = GetSQLHost() 12 | cfg.Port = GetSQLPort() 13 | cfg.Username = GetSQLUser() 14 | cfg.Password = GetSQLPass() 15 | cfg.DB = GetSQLDB() 16 | cfg.SSLMode = GetSQLSSLMode() 17 | return cfg 18 | } 19 | func GetSQLHost() string { 20 | host := "localhost" 21 | if os.Getenv("SQL_HOST") != "" { 22 | host = os.Getenv("SQL_HOST") 23 | } else if config.Config.SQL.Host != "" { 24 | host = config.Config.SQL.Host 25 | } 26 | return host 27 | } 28 | func GetSQLPort() int32 { 29 | port := int32(5432) 30 | envport, err := strconv.Atoi(os.Getenv("SQL_PORT")) 31 | if err == nil && envport != 0 { 32 | port = int32(envport) 33 | } else if config.Config.SQL.Port != 0 { 34 | port = config.Config.SQL.Port 35 | } 36 | return port 37 | } 38 | func GetSQLUser() string { 39 | user := "postgres" 40 | if os.Getenv("SQL_USER") != "" { 41 | user = os.Getenv("SQL_USER") 42 | } else if config.Config.SQL.Username != "" { 43 | user = config.Config.SQL.Username 44 | } 45 | return user 46 | } 47 | func GetSQLPass() string { 48 | pass := "nopass" 49 | if os.Getenv("SQL_PASS") != "" { 50 | pass = os.Getenv("SQL_PASS") 51 | } else if config.Config.SQL.Password != "" { 52 | pass = config.Config.SQL.Password 53 | } 54 | return pass 55 | } 56 | func GetSQLDB() string { 57 | db := "netmaker" 58 | if os.Getenv("SQL_DB") != "" { 59 | db = os.Getenv("SQL_DB") 60 | } else if config.Config.SQL.DB != "" { 61 | db = config.Config.SQL.DB 62 | } 63 | return db 64 | } 65 | func GetSQLSSLMode() string { 66 | sslmode := "disable" 67 | if os.Getenv("SQL_SSL_MODE") != "" { 68 | sslmode = os.Getenv("SQL_SSL_MODE") 69 | } else if config.Config.SQL.SSLMode != "" { 70 | sslmode = config.Config.SQL.SSLMode 71 | } 72 | return sslmode 73 | } 74 | -------------------------------------------------------------------------------- /test/admincreate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | USERNAME="nme" 4 | PASSWORD="testpass" 5 | 6 | generate_post_json () 7 | { 8 | cat <=max) tries. 12 | type RetryStrategy struct { 13 | Wait func(time.Duration) 14 | WaitTime time.Duration 15 | WaitTimeIncrease time.Duration 16 | MaxTries int 17 | Try func() error 18 | OnMaxTries func() 19 | OnSuccess func() 20 | } 21 | 22 | // DoStrategy does the retry strategy specified in the struct, waiting before retrying an operator, 23 | // up to a max number of tries, and if executes a success "finalizer" operation if a retry is successful 24 | func (rs RetryStrategy) DoStrategy() { 25 | err := rs.Try() 26 | if err == nil { 27 | rs.OnSuccess() 28 | return 29 | } 30 | 31 | tries := 1 32 | for { 33 | if tries >= rs.MaxTries { 34 | rs.OnMaxTries() 35 | return 36 | } 37 | rs.Wait(rs.WaitTime) 38 | if err := rs.Try(); err != nil { 39 | tries++ // we tried, increase count 40 | rs.WaitTime += rs.WaitTimeIncrease // for the next time, sleep more 41 | continue // retry 42 | } 43 | rs.OnSuccess() 44 | return 45 | } 46 | } 47 | 48 | func TraceCaller() { 49 | // Skip 1 frame to get the caller of this function 50 | pc, file, line, ok := runtime.Caller(2) 51 | if !ok { 52 | slog.Debug("Unable to get caller information") 53 | return 54 | } 55 | 56 | // Get function name from the program counter (pc) 57 | funcName := runtime.FuncForPC(pc).Name() 58 | 59 | // Print trace details 60 | slog.Debug("Called from function: %s\n", "func-name", funcName) 61 | slog.Debug("File: %s, Line: %d\n", "file", file, "line-no", line) 62 | } 63 | 64 | // NoEmptyStringToCsv takes a bunch of strings, filters out empty ones and returns a csv version of the string 65 | func NoEmptyStringToCsv(strs ...string) string { 66 | var sb strings.Builder 67 | for _, str := range strs { 68 | trimmedStr := strings.TrimSpace(str) 69 | if trimmedStr != "" && trimmedStr != "" { 70 | if sb.Len() > 0 { 71 | sb.WriteString(", ") 72 | } 73 | sb.WriteString(str) 74 | } 75 | } 76 | return sb.String() 77 | } 78 | -------------------------------------------------------------------------------- /validation/validation.go: -------------------------------------------------------------------------------- 1 | package validation 2 | 3 | import ( 4 | "regexp" 5 | 6 | validator "github.com/go-playground/validator/v10" 7 | ) 8 | 9 | // CheckYesOrNo - checks if a field on a struct is yes or no 10 | func CheckYesOrNo(fl validator.FieldLevel) bool { 11 | return fl.Field().String() == "yes" || fl.Field().String() == "no" 12 | } 13 | 14 | // CheckYesOrNoOrUnset - checks if a field is yes, no or unset 15 | func CheckYesOrNoOrUnset(fl validator.FieldLevel) bool { 16 | return CheckYesOrNo(fl) || fl.Field().String() == "unset" 17 | } 18 | 19 | // CheckRegex - check if a struct's field passes regex test 20 | func CheckRegex(fl validator.FieldLevel) bool { 21 | re := regexp.MustCompile(fl.Param()) 22 | return re.MatchString(fl.Field().String()) 23 | } 24 | --------------------------------------------------------------------------------