Merge branch 'netclient_refactor_latest' of https://github.com/gravitl/netmaker into netclient_refactor_peer_updates

This commit is contained in:
Abhishek Kondur
2023-01-05 08:46:49 +05:30
154 changed files with 3995 additions and 478 deletions

View File

@@ -31,6 +31,7 @@ body:
label: Version
description: What version are you running?
options:
- v0.17.1
- v0.17.0
- v0.16.3
- v0.16.2

View File

@@ -55,7 +55,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Setup go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Build
@@ -71,4 +71,3 @@ jobs:
prerelease: true
asset_name: netmaker

View File

@@ -13,7 +13,7 @@ jobs:
docker:
runs-on: ubuntu-latest
steps:
-
-
name: Set tag
run: |
if [[ -n "${{ github.event.inputs.tag }}" ]]; then
@@ -24,16 +24,16 @@ jobs:
TAG="${{ github.ref_name }}"
fi
echo "TAG=${TAG}" >> $GITHUB_ENV
-
-
name: Checkout
uses: actions/checkout@v3
-
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
-
name: Login to DockerHub
uses: docker/login-action@v2
with:
@@ -47,31 +47,33 @@ jobs:
platforms: linux/amd64, linux/arm64, linux/arm/v7
push: true
tags: ${{ github.repository }}:${{ env.TAG }}, ${{ github.repository }}:latest
build-args: version=${{ env.TAG }}
build-args: |
version=${{ env.TAG }}
tags=ce
docker-ee:
runs-on: ubuntu-latest
steps:
-
-
name: Set tag
run: |
if [[ -n "${{ github.event.inputs.tag }}" ]]; then
docker/build-push-action@v3.tag }}
TAG=${{ github.event.inputs.tag }}
elif [[ "${{ github.ref_name }}" == 'master' ]]; then
TAG="latest"
else
TAG="${{ github.ref_name }}"
fi
echo "TAG=${TAG}" >> $GITHUB_ENV
-
-
name: Checkout
uses: actions/checkout@v3
-
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
-
name: Set up Docker Buildx
uses: docker/build-push-action@v3
uses: docker/setup-buildx-action@v2
-
name: Login to DockerHub
uses: docker/login-action@v2
@@ -83,7 +85,9 @@ jobs:
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64, linux/arm64, linux/arm/v7
platforms: linux/amd64, linux/arm64
push: true
tags: ${{ github.repository }}:${{ env.TAG }}-ee
build-args: version=${{ env.TAG }}, tags="-tags=ee"
build-args: |
version=${{ env.TAG }}
tags=ee

View File

@@ -1,8 +1,9 @@
name: Integration Test
on:
workflow_dispatch:
pull_request:
types: [opened, reopened]
types: [opened, synchronize, reopened]
jobs:
build:
@@ -11,7 +12,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Build
@@ -23,13 +24,29 @@ jobs:
env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build main.go
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go
env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
nmctl:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Build
run: |
cd cli
GOOS=linux GOARCH=amd64 go build -o nmctl
GOOS=darwin GOARCH=amd64 go build -o nmctl
GOOS=darwin GOARCH=arm64 go build -o nmctl
GOOS=windows GOARCH=amd64 go build -o nmctl
linux-gui:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Build
@@ -43,7 +60,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Build mac
@@ -55,7 +72,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Mysys2 setup
@@ -75,7 +92,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: run tests

2
.gitignore vendored
View File

@@ -3,6 +3,7 @@ netmaker-arm
netmaker-arm64
netmaker-32
netmaker-amd64
cli/nmctl
netclient/netclient
netclient/netclient.syso
netclient/build
@@ -22,3 +23,4 @@ data/
.vscode/
.idea/
netmaker.exe
netmaker.code-workspace

View File

@@ -7,7 +7,7 @@ COPY . .
ENV GO111MODULE=auto
RUN apk add git
RUN GOOS=linux CGO_ENABLED=1 go build ${tags} -ldflags="-s -X 'main.version=${version}'" .
RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -X 'main.version=${version}'" -tags ${tags} .
# RUN go build -tags=ee . -o netmaker main.go
FROM alpine:3.16.2

View File

@@ -17,7 +17,7 @@
<p align="center">
<a href="https://github.com/gravitl/netmaker/releases">
<img src="https://img.shields.io/badge/Version-0.17.0-informational?style=flat-square" />
<img src="https://img.shields.io/badge/Version-0.17.1-informational?style=flat-square" />
</a>
<a href="https://hub.docker.com/r/gravitl/netmaker/tags">
<img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />
@@ -57,7 +57,7 @@
3. (optional) Prepare DNS - Set a wildcard subdomain in your DNS for Netmaker, e.g. *.netmaker.example.com
4. Run the script:
`sudo wget -qO /root/nm-quick-interactive.sh https://raw.githubusercontent.com/gravitl/netmaker/test_v0.17.0_compose/scripts/nm-quick-interactive.sh && sudo chmod +x /root/nm-quick-interactive.sh && sudo /root/nm-quick-interactive.sh`
`sudo wget -qO /root/nm-quick-interactive.sh https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/nm-quick-interactive.sh && sudo chmod +x /root/nm-quick-interactive.sh && sudo /root/nm-quick-interactive.sh`
This script gives you the option to deploy the Community or Enterprise version of Netmaker. If deploying Enterprise, you get a free account with a 50 node limit by default. It also gives you the option to use your own domain (recommended) or an auto-generated domain.

View File

@@ -3,17 +3,21 @@ package auth
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"time"
"golang.org/x/crypto/bcrypt"
"golang.org/x/oauth2"
"github.com/gorilla/websocket"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/logic/pro/netcache"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/crypto/bcrypt"
"golang.org/x/oauth2"
)
// == consts ==
@@ -30,6 +34,7 @@ const (
auth_key = "netmaker_auth"
user_signin_length = 16
node_signin_length = 64
headless_signin_length = 32
)
// OAuthUser - generic OAuth strategy user
@@ -41,7 +46,10 @@ type OAuthUser struct {
AccessToken string `json:"accesstoken" bson:"accesstoken"`
}
var auth_provider *oauth2.Config
var (
auth_provider *oauth2.Config
upgrader = websocket.Upgrader{}
)
func getCurrentAuthFunctions() map[string]interface{} {
var authInfo = servercfg.GetAuthProviderInfo()
@@ -71,10 +79,6 @@ func InitializeAuthProvider() string {
logger.Log(0, err.Error())
return ""
}
var currentFrontendURL = servercfg.GetFrontendURL()
if currentFrontendURL == "" {
return ""
}
var authInfo = servercfg.GetAuthProviderInfo()
var serverConn = servercfg.GetAPIHost()
if strings.Contains(serverConn, "localhost") || strings.Contains(serverConn, "127.0.0.1") {
@@ -94,12 +98,11 @@ func InitializeAuthProvider() string {
return authInfo[0]
}
// Not included in API reference as part of the OAuth process itself.
// HandleAuthCallback - handles oauth callback
// Note: not included in API reference as part of the OAuth process itself.
func HandleAuthCallback(w http.ResponseWriter, r *http.Request) {
if auth_provider == nil {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintln(w, oauthNotConfigured)
handleOauthNotConfigured(w)
return
}
var functions = getCurrentAuthFunctions()
@@ -108,9 +111,17 @@ func HandleAuthCallback(w http.ResponseWriter, r *http.Request) {
}
state, _ := getStateAndCode(r)
_, err := netcache.Get(state) // if in netcache proceeed with node registration login
if err == nil || len(state) == node_signin_length || (err != nil && strings.Contains(err.Error(), "expired")) {
logger.Log(0, "proceeding with node SSO callback")
HandleNodeSSOCallback(w, r)
if err == nil || errors.Is(err, netcache.ErrExpired) {
switch len(state) {
case node_signin_length:
logger.Log(0, "proceeding with node SSO callback")
HandleNodeSSOCallback(w, r)
case headless_signin_length:
logger.Log(0, "proceeding with headless SSO callback")
HandleHeadlessSSOCallback(w, r)
default:
logger.Log(1, "invalid state length: ", fmt.Sprintf("%d", len(state)))
}
} else { // handle normal login
functions[handle_callback].(func(http.ResponseWriter, *http.Request))(w, r)
}
@@ -120,25 +131,23 @@ func HandleAuthCallback(w http.ResponseWriter, r *http.Request) {
//
// Handles OAuth login.
//
// Schemes: https
// Schemes: https
//
// Security:
// oauth
// Security:
// oauth
func HandleAuthLogin(w http.ResponseWriter, r *http.Request) {
if auth_provider == nil {
var referer = r.Header.Get("referer")
if referer != "" {
http.Redirect(w, r, referer+"login?oauth=callback-error", http.StatusTemporaryRedirect)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintln(w, oauthNotConfigured)
handleOauthNotConfigured(w)
return
}
var functions = getCurrentAuthFunctions()
if functions == nil {
return
}
if servercfg.GetFrontendURL() == "" {
handleOauthNotConfigured(w)
return
}
functions[handle_login].(func(http.ResponseWriter, *http.Request))(w, r)
}
@@ -152,6 +161,80 @@ func IsOauthUser(user *models.User) error {
return bCryptErr
}
// HandleHeadlessSSO - handles the OAuth login flow for headless interfaces such as Netmaker CLI via websocket
func HandleHeadlessSSO(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
logger.Log(0, "error during connection upgrade for headless sign-in:", err.Error())
return
}
if conn == nil {
logger.Log(0, "failed to establish web-socket connection during headless sign-in")
return
}
defer conn.Close()
req := &netcache.CValue{User: "", Pass: ""}
stateStr := logic.RandomString(headless_signin_length)
if err = netcache.Set(stateStr, req); err != nil {
logger.Log(0, "Failed to process sso request -", err.Error())
return
}
timeout := make(chan bool, 1)
answer := make(chan string, 1)
defer close(answer)
defer close(timeout)
if auth_provider == nil {
if err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")); err != nil {
logger.Log(0, "error during message writing:", err.Error())
}
return
}
redirectUrl = fmt.Sprintf("https://%s/api/oauth/register/%s", servercfg.GetAPIConnString(), stateStr)
if err = conn.WriteMessage(websocket.TextMessage, []byte(redirectUrl)); err != nil {
logger.Log(0, "error during message writing:", err.Error())
}
go func() {
for {
cachedReq, err := netcache.Get(stateStr)
if err != nil {
if strings.Contains(err.Error(), "expired") {
logger.Log(0, "timeout occurred while waiting for SSO")
timeout <- true
break
}
continue
} else if cachedReq.Pass != "" {
logger.Log(0, "SSO process completed for user ", cachedReq.User)
answer <- cachedReq.Pass
break
}
time.Sleep(500) // try it 2 times per second to see if auth is completed
}
}()
select {
case result := <-answer:
if err = conn.WriteMessage(websocket.TextMessage, []byte(result)); err != nil {
logger.Log(0, "Error during message writing:", err.Error())
}
case <-timeout:
logger.Log(0, "Authentication server time out for headless SSO login")
if err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")); err != nil {
logger.Log(0, "Error during message writing:", err.Error())
}
}
if err = netcache.Del(stateStr); err != nil {
logger.Log(0, "failed to remove SSO cache entry", err.Error())
}
if err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")); err != nil {
logger.Log(0, "write close:", err.Error())
}
}
// == private methods ==
func addUser(email string) error {
@@ -169,7 +252,7 @@ func addUser(email string) error {
Password: newPass,
}
if !hasAdmin { // must be first attempt, create an admin
if newUser, err = logic.CreateAdmin(newUser); err != nil {
if err = logic.CreateAdmin(&newUser); err != nil {
logger.Log(1, "error creating admin from user,", email, "; user not added")
} else {
logger.Log(1, "admin created from user,", email, "; was first user added")
@@ -177,7 +260,7 @@ func addUser(email string) error {
} else { // otherwise add to db as admin..?
// TODO: add ability to add users with preemptive permissions
newUser.IsAdmin = false
if newUser, err = logic.CreateUser(newUser); err != nil {
if err = logic.CreateUser(&newUser); err != nil {
logger.Log(1, "error creating user,", email, "; user not added")
} else {
logger.Log(0, "user created from ", email)

View File

@@ -37,16 +37,13 @@ func initAzureAD(redirectURL string, clientID string, clientSecret string) {
func handleAzureLogin(w http.ResponseWriter, r *http.Request) {
var oauth_state_string = logic.RandomString(user_signin_length)
if auth_provider == nil && servercfg.GetFrontendURL() != "" {
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
return
} else if auth_provider == nil {
fmt.Fprintf(w, "%s", []byte("no frontend URL was provided and an OAuth login was attempted\nplease reconfigure server to use OAuth or use basic credentials"))
if auth_provider == nil {
handleOauthNotConfigured(w)
return
}
if err := logic.SetState(oauth_state_string); err != nil {
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
handleOauthNotConfigured(w)
return
}
@@ -60,7 +57,7 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
var content, err = getAzureUserInfo(rState, rCode)
if err != nil {
logger.Log(1, "error when getting user info from azure:", err.Error())
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
handleOauthNotConfigured(w)
return
}
_, err = logic.GetUser(content.UserPrincipalName)

View File

@@ -1,5 +1,7 @@
package auth
import "net/http"
// == define error HTML here ==
const oauthNotConfigured = `<!DOCTYPE html><html>
<body>
@@ -7,3 +9,10 @@ const oauthNotConfigured = `<!DOCTYPE html><html>
<p>Please visit the docs <a href="https://docs.netmaker.org/oauth.html" target="_blank" rel="noopener">here</a> to learn how to.</p>
</body>
</html>`
// handleOauthNotConfigured - returns an appropriate html page when oauth is not configured on netmaker server but an oauth login was attempted
func handleOauthNotConfigured(response http.ResponseWriter) {
response.Header().Set("Content-Type", "text/html; charset=utf-8")
response.WriteHeader(http.StatusInternalServerError)
response.Write([]byte(oauthNotConfigured))
}

View File

@@ -37,16 +37,13 @@ func initGithub(redirectURL string, clientID string, clientSecret string) {
func handleGithubLogin(w http.ResponseWriter, r *http.Request) {
var oauth_state_string = logic.RandomString(user_signin_length)
if auth_provider == nil && servercfg.GetFrontendURL() != "" {
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
return
} else if auth_provider == nil {
fmt.Fprintf(w, "%s", []byte("no frontend URL was provided and an OAuth login was attempted\nplease reconfigure server to use OAuth or use basic credentials"))
if auth_provider == nil {
handleOauthNotConfigured(w)
return
}
if err := logic.SetState(oauth_state_string); err != nil {
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
handleOauthNotConfigured(w)
return
}
@@ -60,7 +57,7 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
var content, err = getGithubUserInfo(rState, rCode)
if err != nil {
logger.Log(1, "error when getting user info from github:", err.Error())
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
handleOauthNotConfigured(w)
return
}
_, err = logic.GetUser(content.Login)

View File

@@ -38,16 +38,13 @@ func initGoogle(redirectURL string, clientID string, clientSecret string) {
func handleGoogleLogin(w http.ResponseWriter, r *http.Request) {
var oauth_state_string = logic.RandomString(user_signin_length)
if auth_provider == nil && servercfg.GetFrontendURL() != "" {
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
return
} else if auth_provider == nil {
fmt.Fprintf(w, "%s", []byte("no frontend URL was provided and an OAuth login was attempted\nplease reconfigure server to use OAuth or use basic credentials"))
if auth_provider == nil {
handleOauthNotConfigured(w)
return
}
if err := logic.SetState(oauth_state_string); err != nil {
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
handleOauthNotConfigured(w)
return
}
@@ -62,7 +59,7 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
var content, err = getGoogleUserInfo(rState, rCode)
if err != nil {
logger.Log(1, "error when getting user info from google:", err.Error())
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
handleOauthNotConfigured(w)
return
}
_, err = logic.GetUser(content.Email)

93
auth/headless_callback.go Normal file
View File

@@ -0,0 +1,93 @@
package auth
import (
"bytes"
"fmt"
"net/http"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/logic/pro/netcache"
"github.com/gravitl/netmaker/models"
)
// HandleHeadlessSSOCallback - handle OAuth callback for headless logins such as Netmaker CLI
func HandleHeadlessSSOCallback(w http.ResponseWriter, r *http.Request) {
functions := getCurrentAuthFunctions()
if functions == nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("bad conf"))
logger.Log(0, "Missing Oauth config in HandleHeadlessSSOCallback")
return
}
state, code := getStateAndCode(r)
userClaims, err := functions[get_user_info].(func(string, string) (*OAuthUser, error))(state, code)
if err != nil {
logger.Log(0, "error when getting user info from callback:", err.Error())
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Failed to retrieve OAuth user claims"))
return
}
if code == "" || state == "" {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Wrong params"))
logger.Log(0, "Missing params in HandleHeadlessSSOCallback")
return
}
// all responses should be in html format from here on out
w.Header().Add("content-type", "text/html; charset=utf-8")
// retrieve machinekey from state cache
reqKeyIf, machineKeyFoundErr := netcache.Get(state)
if machineKeyFoundErr != nil {
logger.Log(0, "requested machine state key expired before authorisation completed -", machineKeyFoundErr.Error())
response := returnErrTemplate("", "requested machine state key expired before authorisation completed", state, reqKeyIf)
w.WriteHeader(http.StatusInternalServerError)
w.Write(response)
return
}
_, err = logic.GetUser(userClaims.getUserName())
if err != nil { // user must not exists, so try to make one
if err = addUser(userClaims.getUserName()); err != nil {
logger.Log(1, "could not create new user: ", userClaims.getUserName())
return
}
}
newPass, fetchErr := fetchPassValue("")
if fetchErr != nil {
return
}
jwt, jwtErr := logic.VerifyAuthRequest(models.UserAuthParams{
UserName: userClaims.getUserName(),
Password: newPass,
})
if jwtErr != nil {
logger.Log(1, "could not parse jwt for user", userClaims.getUserName())
return
}
logger.Log(1, "headless SSO login by user:", userClaims.getUserName())
// Send OK to user in the browser
var response bytes.Buffer
if err := ssoCallbackTemplate.Execute(&response, ssoCallbackTemplateConfig{
User: userClaims.getUserName(),
Verb: "Authenticated",
}); err != nil {
logger.Log(0, "Could not render SSO callback template ", err.Error())
response := returnErrTemplate(userClaims.getUserName(), "Could not render SSO callback template", state, reqKeyIf)
w.WriteHeader(http.StatusInternalServerError)
w.Write(response)
} else {
w.WriteHeader(http.StatusOK)
w.Write(response.Bytes())
}
reqKeyIf.Pass = fmt.Sprintf("JWT: %s", jwt)
if err = netcache.Set(state, reqKeyIf); err != nil {
logger.Log(0, "failed to set netcache for user", reqKeyIf.User, "-", err.Error())
}
}

View File

@@ -13,7 +13,6 @@ import (
"github.com/gravitl/netmaker/logic/pro/netcache"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/models/promodels"
"github.com/gravitl/netmaker/servercfg"
)
var (
@@ -41,7 +40,7 @@ func HandleNodeSSOCallback(w http.ResponseWriter, r *http.Request) {
var userClaims, err = functions[get_user_info].(func(string, string) (*OAuthUser, error))(state, code)
if err != nil {
logger.Log(0, "error when getting user info from callback:", err.Error())
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
handleOauthNotConfigured(w)
return
}
@@ -135,7 +134,9 @@ func setNetcache(ncache *netcache.CValue, state string) error {
func returnErrTemplate(uname, message, state string, ncache *netcache.CValue) []byte {
var response bytes.Buffer
ncache.Pass = message
if ncache != nil {
ncache.Pass = message
}
err := ssoErrCallbackTemplate.Execute(&response, ssoCallbackTemplateConfig{
User: uname,
Verb: message,
@@ -257,5 +258,5 @@ func isUserIsAllowed(username, network string, shouldAddUser bool) (*models.User
}
}
return &user, nil
return user, nil
}

View File

@@ -1,7 +1,6 @@
package auth
import (
"encoding/hex"
"encoding/json"
"fmt"
"strings"
@@ -45,7 +44,7 @@ func SessionHandler(conn *websocket.Conn) {
req.Pass = ""
req.User = ""
// Add any extra parameter provided in the configuration to the Authorize Endpoint request??
stateStr := hex.EncodeToString([]byte(logic.RandomString(node_signin_length)))
stateStr := logic.RandomString(node_signin_length)
if err := netcache.Set(stateStr, req); err != nil {
logger.Log(0, "Failed to process sso request -", err.Error())
return

View File

@@ -50,16 +50,13 @@ func initOIDC(redirectURL string, clientID string, clientSecret string, issuer s
func handleOIDCLogin(w http.ResponseWriter, r *http.Request) {
var oauth_state_string = logic.RandomString(user_signin_length)
if auth_provider == nil && servercfg.GetFrontendURL() != "" {
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
return
} else if auth_provider == nil {
fmt.Fprintf(w, "%s", []byte("no frontend URL was provided and an OAuth login was attempted\nplease reconfigure server to use OAuth or use basic credentials"))
if auth_provider == nil {
handleOauthNotConfigured(w)
return
}
if err := logic.SetState(oauth_state_string); err != nil {
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
handleOauthNotConfigured(w)
return
}
var url = auth_provider.AuthCodeURL(oauth_state_string)
@@ -73,7 +70,7 @@ func handleOIDCCallback(w http.ResponseWriter, r *http.Request) {
var content, err = getOIDCUserInfo(rState, rCode)
if err != nil {
logger.Log(1, "error when getting user info from callback:", err.Error())
http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?oauth=callback-error", http.StatusTemporaryRedirect)
handleOauthNotConfigured(w)
return
}
_, err = logic.GetUser(content.Email)

46
cli/cmd/acl/allow.go Normal file
View File

@@ -0,0 +1,46 @@
package acl
import (
"fmt"
"log"
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/logic/acls"
"github.com/spf13/cobra"
)
var aclAllowCmd = &cobra.Command{
Use: "allow [NETWORK NAME] [FROM_NODE_NAME] [TO_NODE_NAME]",
Args: cobra.ExactArgs(3),
Short: "Allow access from one node to another",
Long: `Allow access from one node to another`,
Run: func(cmd *cobra.Command, args []string) {
nameIDMap := make(map[string]string)
for _, node := range *functions.GetNodes(args[0]) {
nameIDMap[strings.ToLower(node.Name)] = node.ID
}
fromNodeID, ok := nameIDMap[strings.ToLower(args[1])]
if !ok {
log.Fatalf("Node %s doesn't exist", args[1])
}
toNodeID, ok := nameIDMap[strings.ToLower(args[2])]
if !ok {
log.Fatalf("Node %s doesn't exist", args[2])
}
payload := acls.ACLContainer(map[acls.AclID]acls.ACL{
acls.AclID(fromNodeID): map[acls.AclID]byte{
acls.AclID(toNodeID): acls.Allowed,
},
acls.AclID(toNodeID): map[acls.AclID]byte{
acls.AclID(fromNodeID): acls.Allowed,
},
})
functions.UpdateACL(args[0], &payload)
fmt.Println("Success")
},
}
func init() {
rootCmd.AddCommand(aclAllowCmd)
}

46
cli/cmd/acl/deny.go Normal file
View File

@@ -0,0 +1,46 @@
package acl
import (
"fmt"
"log"
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/logic/acls"
"github.com/spf13/cobra"
)
var aclDenyCmd = &cobra.Command{
Use: "deny [NETWORK NAME] [FROM_NODE_NAME] [TO_NODE_NAME]",
Args: cobra.ExactArgs(3),
Short: "Deny access from one node to another",
Long: `Deny access from one node to another`,
Run: func(cmd *cobra.Command, args []string) {
nameIDMap := make(map[string]string)
for _, node := range *functions.GetNodes(args[0]) {
nameIDMap[strings.ToLower(node.Name)] = node.ID
}
fromNodeID, ok := nameIDMap[strings.ToLower(args[1])]
if !ok {
log.Fatalf("Node %s doesn't exist", args[1])
}
toNodeID, ok := nameIDMap[strings.ToLower(args[2])]
if !ok {
log.Fatalf("Node %s doesn't exist", args[2])
}
payload := acls.ACLContainer(map[acls.AclID]acls.ACL{
acls.AclID(fromNodeID): map[acls.AclID]byte{
acls.AclID(toNodeID): acls.NotAllowed,
},
acls.AclID(toNodeID): map[acls.AclID]byte{
acls.AclID(fromNodeID): acls.NotAllowed,
},
})
functions.UpdateACL(args[0], &payload)
fmt.Println("Success")
},
}
func init() {
rootCmd.AddCommand(aclDenyCmd)
}

46
cli/cmd/acl/list.go Normal file
View File

@@ -0,0 +1,46 @@
package acl
import (
"os"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/logic/acls"
"github.com/guumaster/tablewriter"
"github.com/spf13/cobra"
)
var aclListCmd = &cobra.Command{
Use: "list [NETWORK NAME]",
Args: cobra.ExactArgs(1),
Short: "List all ACLs associated with a network",
Long: `List all ACLs associated with a network`,
Run: func(cmd *cobra.Command, args []string) {
aclSource := (map[acls.AclID]acls.ACL)(*functions.GetACL(args[0]))
nodes := functions.GetNodes(args[0])
idNameMap := make(map[string]string)
for _, node := range *nodes {
idNameMap[node.ID] = node.Name
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"From", "To", "Status"})
for id, acl := range aclSource {
for k, v := range (map[acls.AclID]byte)(acl) {
row := []string{idNameMap[string(id)], idNameMap[string(k)]}
switch v {
case acls.NotAllowed:
row = append(row, "Not Allowed")
case acls.NotPresent:
row = append(row, "Not Present")
case acls.Allowed:
row = append(row, "Allowed")
}
table.Append(row)
}
}
table.Render()
},
}
func init() {
rootCmd.AddCommand(aclListCmd)
}

38
cli/cmd/acl/root.go Normal file
View File

@@ -0,0 +1,38 @@
package acl
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "acl",
Short: "Manage Access Control Lists (ACLs)",
Long: `Manage Access Control Lists (ACLs)`,
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

20
cli/cmd/context/delete.go Normal file
View File

@@ -0,0 +1,20 @@
package context
import (
"github.com/gravitl/netmaker/cli/config"
"github.com/spf13/cobra"
)
var contextDeleteCmd = &cobra.Command{
Use: "delete [NAME]",
Args: cobra.ExactArgs(1),
Short: "Delete a context",
Long: `Delete a context`,
Run: func(cmd *cobra.Command, args []string) {
config.DeleteContext(args[0])
},
}
func init() {
rootCmd.AddCommand(contextDeleteCmd)
}

20
cli/cmd/context/list.go Normal file
View File

@@ -0,0 +1,20 @@
package context
import (
"github.com/gravitl/netmaker/cli/config"
"github.com/spf13/cobra"
)
var contextListCmd = &cobra.Command{
Use: "list",
Args: cobra.NoArgs,
Short: "List all contexts",
Long: `List all contexts`,
Run: func(cmd *cobra.Command, args []string) {
config.ListAll()
},
}
func init() {
rootCmd.AddCommand(contextListCmd)
}

38
cli/cmd/context/root.go Normal file
View File

@@ -0,0 +1,38 @@
package context
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "context",
Short: "Manage various netmaker server configurations",
Long: `Manage various netmaker server configurations`,
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

48
cli/cmd/context/set.go Normal file
View File

@@ -0,0 +1,48 @@
package context
import (
"log"
"github.com/gravitl/netmaker/cli/config"
"github.com/spf13/cobra"
)
var (
endpoint string
username string
password string
masterKey string
sso bool
)
var contextSetCmd = &cobra.Command{
Use: "set [NAME]",
Args: cobra.ExactArgs(1),
Short: "Create a context or update an existing one",
Long: `Create a context or update an existing one`,
Run: func(cmd *cobra.Command, args []string) {
ctx := config.Context{
Endpoint: endpoint,
Username: username,
Password: password,
MasterKey: masterKey,
SSO: sso,
}
if ctx.Username == "" && ctx.MasterKey == "" && !ctx.SSO {
cmd.Usage()
log.Fatal("Either username/password or master key is required")
}
config.SetContext(args[0], ctx)
},
}
func init() {
contextSetCmd.Flags().StringVar(&endpoint, "endpoint", "", "Endpoint of the API Server")
contextSetCmd.MarkFlagRequired("endpoint")
contextSetCmd.Flags().StringVar(&username, "username", "", "Username")
contextSetCmd.Flags().StringVar(&password, "password", "", "Password")
contextSetCmd.MarkFlagsRequiredTogether("username", "password")
contextSetCmd.Flags().BoolVar(&sso, "sso", false, "Login via Single Sign On (SSO) ?")
contextSetCmd.Flags().StringVar(&masterKey, "master_key", "", "Master Key")
rootCmd.AddCommand(contextSetCmd)
}

20
cli/cmd/context/use.go Normal file
View File

@@ -0,0 +1,20 @@
package context
import (
"github.com/gravitl/netmaker/cli/config"
"github.com/spf13/cobra"
)
var contextUseCmd = &cobra.Command{
Use: "use [NAME]",
Args: cobra.ExactArgs(1),
Short: "Set the current context",
Long: `Set the current context`,
Run: func(cmd *cobra.Command, args []string) {
config.SetCurrentContext(args[0])
},
}
func init() {
rootCmd.AddCommand(contextUseCmd)
}

33
cli/cmd/dns/create.go Normal file
View File

@@ -0,0 +1,33 @@
package dns
import (
"log"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/spf13/cobra"
)
var dnsCreateCmd = &cobra.Command{
Use: "create",
Args: cobra.NoArgs,
Short: "Create a DNS entry",
Long: `Create a DNS entry`,
Run: func(cmd *cobra.Command, args []string) {
if address == "" && address6 == "" {
log.Fatal("Either IPv4 or IPv6 address is required")
}
dnsEntry := &models.DNSEntry{Name: dnsName, Address: address, Address6: address6, Network: networkName}
functions.PrettyPrint(functions.CreateDNS(networkName, dnsEntry))
},
}
func init() {
dnsCreateCmd.Flags().StringVar(&dnsName, "name", "", "Name of the DNS entry")
dnsCreateCmd.MarkFlagRequired("name")
dnsCreateCmd.Flags().StringVar(&networkName, "network", "", "Name of the Network")
dnsCreateCmd.MarkFlagRequired("network")
dnsCreateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 Address")
dnsCreateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 Address")
rootCmd.AddCommand(dnsCreateCmd)
}

20
cli/cmd/dns/delete.go Normal file
View File

@@ -0,0 +1,20 @@
package dns
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var dnsDeleteCmd = &cobra.Command{
Use: "delete [NETWORK NAME] [DOMAIN NAME]",
Args: cobra.ExactArgs(2),
Short: "Delete a DNS entry",
Long: `Delete a DNS entry`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.DeleteDNS(args[0], args[1]))
},
}
func init() {
rootCmd.AddCommand(dnsDeleteCmd)
}

9
cli/cmd/dns/flags.go Normal file
View File

@@ -0,0 +1,9 @@
package dns
var (
dnsName string
address string
address6 string
networkName string
dnsType string
)

47
cli/cmd/dns/list.go Normal file
View File

@@ -0,0 +1,47 @@
package dns
import (
"fmt"
"os"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/guumaster/tablewriter"
"github.com/spf13/cobra"
)
var dnsListCmd = &cobra.Command{
Use: "list",
Args: cobra.NoArgs,
Short: "List DNS entries",
Long: `List DNS entries`,
Run: func(cmd *cobra.Command, args []string) {
var data []models.DNSEntry
if networkName != "" {
switch dnsType {
case "node":
data = *functions.GetNodeDNS(networkName)
case "custom":
data = *functions.GetCustomDNS(networkName)
case "network", "":
data = *functions.GetNetworkDNS(networkName)
default:
fmt.Println("Invalid DNS type provided ", dnsType)
}
} else {
data = *functions.GetDNS()
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Network", "IPv4 Address", "IPv6 Address"})
for _, d := range data {
table.Append([]string{d.Name, d.Network, d.Address, d.Address6})
}
table.Render()
},
}
func init() {
dnsListCmd.Flags().StringVar(&networkName, "network", "", "Network name")
dnsListCmd.Flags().StringVar(&dnsType, "type", "", "Type of DNS records to fetch ENUM(node, custom, network)")
rootCmd.AddCommand(dnsListCmd)
}

22
cli/cmd/dns/push.go Normal file
View File

@@ -0,0 +1,22 @@
package dns
import (
"fmt"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var dnsPushCmd = &cobra.Command{
Use: "push",
Args: cobra.NoArgs,
Short: "Push latest DNS entries",
Long: `Push latest DNS entries`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(*functions.PushDNS())
},
}
func init() {
rootCmd.AddCommand(dnsPushCmd)
}

38
cli/cmd/dns/root.go Normal file
View File

@@ -0,0 +1,38 @@
package dns
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "dns",
Short: "Manage DNS entries associated with a network",
Long: `Manage DNS entries associated with a network`,
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

View File

@@ -0,0 +1,22 @@
package ext_client
import (
"fmt"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var extClientConfigCmd = &cobra.Command{
Use: "config [NETWORK NAME] [EXTERNAL CLIENT ID]",
Args: cobra.ExactArgs(2),
Short: "Get an External Client Configuration",
Long: `Get an External Client Configuration`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(functions.GetExtClientConfig(args[0], args[1]))
},
}
func init() {
rootCmd.AddCommand(extClientConfigCmd)
}

View File

@@ -0,0 +1,26 @@
package ext_client
import (
"fmt"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var extClientID string
var extClientCreateCmd = &cobra.Command{
Use: "create [NETWORK NAME] [NODE ID]",
Args: cobra.ExactArgs(2),
Short: "Create an External Client",
Long: `Create an External Client`,
Run: func(cmd *cobra.Command, args []string) {
functions.CreateExtClient(args[0], args[1], extClientID)
fmt.Println("Success")
},
}
func init() {
extClientCreateCmd.Flags().StringVar(&extClientID, "id", "", "ID of the external client")
rootCmd.AddCommand(extClientCreateCmd)
}

View File

@@ -0,0 +1,20 @@
package ext_client
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var extClientDeleteCmd = &cobra.Command{
Use: "delete [NETWORK NAME] [EXTERNAL CLIENT ID]",
Args: cobra.ExactArgs(2),
Short: "Delete an External Client",
Long: `Delete an External Client`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.DeleteExtClient(args[0], args[1]))
},
}
func init() {
rootCmd.AddCommand(extClientDeleteCmd)
}

20
cli/cmd/ext_client/get.go Normal file
View File

@@ -0,0 +1,20 @@
package ext_client
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var extClientGetCmd = &cobra.Command{
Use: "get [NETWORK NAME] [EXTERNAL CLIENT ID]",
Args: cobra.ExactArgs(2),
Short: "Get an External Client",
Long: `Get an External Client`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.GetExtClient(args[0], args[1]))
},
}
func init() {
rootCmd.AddCommand(extClientGetCmd)
}

View File

@@ -0,0 +1,40 @@
package ext_client
import (
"os"
"strconv"
"time"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/guumaster/tablewriter"
"github.com/spf13/cobra"
)
var networkName string
var extClientListCmd = &cobra.Command{
Use: "list",
Args: cobra.NoArgs,
Short: "List External Clients",
Long: `List External Clients`,
Run: func(cmd *cobra.Command, args []string) {
var data []models.ExtClient
if networkName != "" {
data = *functions.GetNetworkExtClients(networkName)
} else {
data = *functions.GetAllExtClients()
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Client ID", "Network", "IPv4 Address", "IPv6 Address", "Enabled", "Last Modified"})
for _, d := range data {
table.Append([]string{d.ClientID, d.Network, d.Address, d.Address6, strconv.FormatBool(d.Enabled), time.Unix(d.LastModified, 0).String()})
}
table.Render()
},
}
func init() {
extClientListCmd.Flags().StringVar(&networkName, "network", "", "Network name")
rootCmd.AddCommand(extClientListCmd)
}

View File

@@ -0,0 +1,38 @@
package ext_client
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "ext_client",
Short: "Manage External Clients",
Long: `Manage External Clients`,
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

View File

@@ -0,0 +1,71 @@
package ext_client
import (
"encoding/json"
"log"
"os"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/spf13/cobra"
)
var (
extClientUpdateFile string
description string
privateKey string
publicKey string
address string
address6 string
ingressGatewayID string
ingressGatewayEndpoint string
ownerID string
)
var extClientUpdateCmd = &cobra.Command{
Use: "update [NETWORK NAME] [EXTERNAL CLIENT ID]",
Args: cobra.ExactArgs(2),
Short: "Update an External Client",
Long: `Update an External Client`,
Run: func(cmd *cobra.Command, args []string) {
var (
network = args[0]
clientID = args[1]
extClient = &models.ExtClient{}
)
if extClientUpdateFile != "" {
content, err := os.ReadFile(extClientUpdateFile)
if err != nil {
log.Fatal("Error when opening file: ", err)
}
if err := json.Unmarshal(content, extClient); err != nil {
log.Fatal(err)
}
} else {
extClient.ClientID = clientID
extClient.Description = description
extClient.PrivateKey = privateKey
extClient.PublicKey = publicKey
extClient.Network = network
extClient.Address = address
extClient.Address6 = address6
extClient.IngressGatewayID = ingressGatewayID
extClient.IngressGatewayEndpoint = ingressGatewayEndpoint
extClient.OwnerID = ownerID
}
functions.PrettyPrint(functions.UpdateExtClient(network, clientID, extClient))
},
}
func init() {
extClientUpdateCmd.Flags().StringVar(&extClientUpdateFile, "file", "", "Filepath of updated external client definition in JSON")
extClientUpdateCmd.Flags().StringVar(&description, "desc", "", "Description of the external client")
extClientUpdateCmd.Flags().StringVar(&privateKey, "private_key", "", "Filepath of updated external client definition in JSON")
extClientUpdateCmd.Flags().StringVar(&publicKey, "public_key", "", "Filepath of updated external client definition in JSON")
extClientUpdateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 address of the external client")
extClientUpdateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 address of the external client")
extClientUpdateCmd.Flags().StringVar(&ingressGatewayID, "ingress_gateway_id", "", "ID of the ingress gateway")
extClientUpdateCmd.Flags().StringVar(&ingressGatewayEndpoint, "ingress_gateway_endpoint", "", "Endpoint of the ingress gateway")
extClientUpdateCmd.Flags().StringVar(&ownerID, "owner_id", "", "External Client owner's ID")
rootCmd.AddCommand(extClientUpdateCmd)
}

35
cli/cmd/keys/create.go Normal file
View File

@@ -0,0 +1,35 @@
package keys
import (
"log"
"strconv"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/spf13/cobra"
)
var keyName string
var keysCreateCmd = &cobra.Command{
Use: "create [NETWORK NAME] [NUM USES]",
Args: cobra.ExactArgs(2),
Short: "Create an access key",
Long: `Create an access key`,
Run: func(cmd *cobra.Command, args []string) {
keyUses, err := strconv.ParseInt(args[1], 10, 64)
if err != nil {
log.Fatal(err)
}
key := &models.AccessKey{Uses: int(keyUses)}
if keyName != "" {
key.Name = keyName
}
functions.PrettyPrint(functions.CreateKey(args[0], key))
},
}
func init() {
keysCreateCmd.Flags().StringVar(&keyName, "name", "", "Name of the key")
rootCmd.AddCommand(keysCreateCmd)
}

23
cli/cmd/keys/delete.go Normal file
View File

@@ -0,0 +1,23 @@
package keys
import (
"fmt"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var keysDeleteCmd = &cobra.Command{
Use: "delete [NETWORK NAME] [KEY NAME]",
Args: cobra.ExactArgs(2),
Short: "Delete a key",
Long: `Delete a key`,
Run: func(cmd *cobra.Command, args []string) {
functions.DeleteKey(args[0], args[1])
fmt.Println("Success")
},
}
func init() {
rootCmd.AddCommand(keysDeleteCmd)
}

20
cli/cmd/keys/list.go Normal file
View File

@@ -0,0 +1,20 @@
package keys
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var keysListCmd = &cobra.Command{
Use: "list [NETWORK NAME]",
Args: cobra.ExactArgs(1),
Short: "List all keys associated with a network",
Long: `List all keys associated with a network`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.GetKeys(args[0]))
},
}
func init() {
rootCmd.AddCommand(keysListCmd)
}

38
cli/cmd/keys/root.go Normal file
View File

@@ -0,0 +1,38 @@
package keys
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "keys",
Short: "Manage access keys associated with a network",
Long: `Manage access keys associated with a network`,
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

22
cli/cmd/logs.go Normal file
View File

@@ -0,0 +1,22 @@
package cmd
import (
"fmt"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var getLogsCmd = &cobra.Command{
Use: "logs",
Args: cobra.NoArgs,
Short: "Retrieve server logs",
Long: `Retrieve server logs`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(functions.GetLogs())
},
}
func init() {
rootCmd.AddCommand(getLogsCmd)
}

20
cli/cmd/metrics/all.go Normal file
View File

@@ -0,0 +1,20 @@
package metrics
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var metricsAllCmd = &cobra.Command{
Use: "all",
Args: cobra.NoArgs,
Short: "Retrieve all metrics",
Long: `Retrieve all metrics`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.GetAllMetrics())
},
}
func init() {
rootCmd.AddCommand(metricsAllCmd)
}

View File

@@ -0,0 +1,20 @@
package metrics
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var metricsNetworkCmd = &cobra.Command{
Use: "network [NETWORK NAME]",
Args: cobra.ExactArgs(1),
Short: "Retrieve network metrics",
Long: `Retrieve network metrics`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.GetNetworkNodeMetrics(args[0]))
},
}
func init() {
rootCmd.AddCommand(metricsNetworkCmd)
}

View File

@@ -0,0 +1,20 @@
package metrics
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var metricsNetworkExtCmd = &cobra.Command{
Use: "network_ext [NETWORK NAME]",
Args: cobra.ExactArgs(1),
Short: "Retrieve metrics of external clients on a given network",
Long: `Retrieve metrics of external clients on a given network`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.GetNetworkExtMetrics(args[0]))
},
}
func init() {
rootCmd.AddCommand(metricsNetworkExtCmd)
}

20
cli/cmd/metrics/node.go Normal file
View File

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

38
cli/cmd/metrics/root.go Normal file
View File

@@ -0,0 +1,38 @@
package metrics
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "metrics",
Short: "Fetch metrics of nodes/networks",
Long: `Fetch metrics of nodes/networks`,
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

85
cli/cmd/network/create.go Normal file
View File

@@ -0,0 +1,85 @@
package network
import (
"encoding/json"
"log"
"os"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/spf13/cobra"
)
var networkCreateCmd = &cobra.Command{
Use: "create",
Short: "Create a Network",
Long: `Create a Network`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
network := &models.Network{}
if networkDefinitionFilePath != "" {
content, err := os.ReadFile(networkDefinitionFilePath)
if err != nil {
log.Fatal("Error when opening file: ", err)
}
if err := json.Unmarshal(content, network); err != nil {
log.Fatal(err)
}
} else {
network.NetID = netID
network.AddressRange = address
if address6 != "" {
network.AddressRange6 = address6
network.IsIPv6 = "yes"
}
if udpHolePunch {
network.DefaultUDPHolePunch = "yes"
}
if localNetwork {
network.IsLocal = "yes"
}
if defaultACL {
network.DefaultACL = "yes"
}
if pointToSite {
network.IsPointToSite = "yes"
}
network.DefaultInterface = defaultInterface
network.DefaultListenPort = int32(defaultListenPort)
network.NodeLimit = int32(nodeLimit)
network.DefaultPostUp = defaultPostUp
network.DefaultPostDown = defaultPostDown
network.DefaultKeepalive = int32(defaultKeepalive)
if allowManualSignUp {
network.AllowManualSignUp = "yes"
}
network.LocalRange = localRange
network.DefaultExtClientDNS = defaultExtClientDNS
network.DefaultMTU = int32(defaultMTU)
}
functions.PrettyPrint(functions.CreateNetwork(network))
},
}
func init() {
networkCreateCmd.Flags().StringVar(&networkDefinitionFilePath, "file", "", "Path to network_definition.json")
networkCreateCmd.Flags().StringVar(&netID, "name", "", "Name of the network")
networkCreateCmd.MarkFlagsMutuallyExclusive("file", "name")
networkCreateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 address of the network")
networkCreateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 address of the network")
networkCreateCmd.Flags().BoolVar(&udpHolePunch, "udp_hole_punch", false, "Enable UDP Hole Punching ?")
networkCreateCmd.Flags().BoolVar(&localNetwork, "local", false, "Is the network local (LAN) ?")
networkCreateCmd.Flags().BoolVar(&defaultACL, "default_acl", false, "Enable default Access Control List ?")
networkCreateCmd.Flags().BoolVar(&pointToSite, "point_to_site", false, "Enforce all clients to have only 1 central peer ?")
networkCreateCmd.Flags().StringVar(&defaultInterface, "interface", "", "Name of the network interface")
networkCreateCmd.Flags().StringVar(&defaultPostUp, "post_up", "", "Commands to run after server is up `;` separated")
networkCreateCmd.Flags().StringVar(&defaultPostDown, "post_down", "", "Commands to run after server is down `;` separated")
networkCreateCmd.Flags().StringVar(&localRange, "local_range", "", "Local CIDR range")
networkCreateCmd.Flags().StringVar(&defaultExtClientDNS, "ext_client_dns", "", "IPv4 address of DNS server to be used by external clients")
networkCreateCmd.Flags().IntVar(&defaultListenPort, "listen_port", 51821, "Default wireguard port each node will attempt to use")
networkCreateCmd.Flags().IntVar(&nodeLimit, "node_limit", 999999999, "Maximum number of nodes that can be associated with this network")
networkCreateCmd.Flags().IntVar(&defaultKeepalive, "keep_alive", 20, "Keep Alive in seconds")
networkCreateCmd.Flags().IntVar(&defaultMTU, "mtu", 1280, "MTU size")
networkCreateCmd.Flags().BoolVar(&allowManualSignUp, "manual_signup", false, "Allow manual signup ?")
rootCmd.AddCommand(networkCreateCmd)
}

22
cli/cmd/network/delete.go Normal file
View File

@@ -0,0 +1,22 @@
package network
import (
"fmt"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var networkDeleteCmd = &cobra.Command{
Use: "delete [NAME]",
Short: "Delete a Network",
Long: `Delete a Network`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(*functions.DeleteNetwork(args[0]))
},
}
func init() {
rootCmd.AddCommand(networkDeleteCmd)
}

22
cli/cmd/network/flags.go Normal file
View File

@@ -0,0 +1,22 @@
package network
var (
networkDefinitionFilePath string
netID string
address string
address6 string
udpHolePunch bool
localNetwork bool
defaultACL bool
pointToSite bool
defaultInterface string
defaultListenPort int
nodeLimit int
defaultPostUp string
defaultPostDown string
defaultKeepalive int
allowManualSignUp bool
localRange string
defaultExtClientDNS string
defaultMTU int
)

20
cli/cmd/network/get.go Normal file
View File

@@ -0,0 +1,20 @@
package network
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var networkGetCmd = &cobra.Command{
Use: "get [NETWORK NAME]",
Short: "Get a Network",
Long: `Get a Network`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.GetNetwork(args[0]))
},
}
func init() {
rootCmd.AddCommand(networkGetCmd)
}

32
cli/cmd/network/list.go Normal file
View File

@@ -0,0 +1,32 @@
package network
import (
"os"
"time"
"github.com/gravitl/netmaker/cli/functions"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
)
var networkListCmd = &cobra.Command{
Use: "list",
Short: "List all Networks",
Long: `List all Networks`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
networks := functions.GetNetworks()
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"NetId", "Address Range (IPv4)", "Address Range (IPv6)", "Network Last Modified", "Nodes Last Modified"})
for _, n := range *networks {
networkLastModified := time.Unix(n.NetworkLastModified, 0).Format(time.RFC3339)
nodesLastModified := time.Unix(n.NodesLastModified, 0).Format(time.RFC3339)
table.Append([]string{n.NetID, n.AddressRange, n.AddressRange6, networkLastModified, nodesLastModified})
}
table.Render()
},
}
func init() {
rootCmd.AddCommand(networkListCmd)
}

View File

@@ -0,0 +1,27 @@
package network
import (
"log"
"strconv"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var networkNodeLimitCmd = &cobra.Command{
Use: "node_limit [NETWORK NAME] [NEW LIMIT]",
Short: "Update network nodel limit",
Long: `Update network nodel limit`,
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
nodelimit, err := strconv.ParseInt(args[1], 10, 32)
if err != nil {
log.Fatal(err)
}
functions.PrettyPrint(functions.UpdateNetworkNodeLimit(args[0], int32(nodelimit)))
},
}
func init() {
rootCmd.AddCommand(networkNodeLimitCmd)
}

View File

@@ -0,0 +1,20 @@
package network
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var networkRefreshKeysCmd = &cobra.Command{
Use: "refresh_keys [NETWORK NAME]",
Short: "Refresh public and private key pairs of a network",
Long: `Refresh public and private key pairs of a network`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.RefreshKeys(args[0]))
},
}
func init() {
rootCmd.AddCommand(networkRefreshKeysCmd)
}

40
cli/cmd/network/root.go Normal file
View File

@@ -0,0 +1,40 @@
package network
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "network",
Short: "Manage Netmaker Networks",
Long: `Manage Netmaker Networks`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

86
cli/cmd/network/update.go Normal file
View File

@@ -0,0 +1,86 @@
package network
import (
"encoding/json"
"log"
"os"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/spf13/cobra"
)
var networkUpdateCmd = &cobra.Command{
Use: "update [NETWORK NAME]",
Short: "Update a Network",
Long: `Update a Network`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var (
networkName = args[0]
network = &models.Network{}
)
if networkDefinitionFilePath != "" {
content, err := os.ReadFile(networkDefinitionFilePath)
if err != nil {
log.Fatal("Error when opening file: ", err)
}
if err := json.Unmarshal(content, network); err != nil {
log.Fatal(err)
}
} else {
network.NetID = networkName
network.AddressRange = address
if address6 != "" {
network.AddressRange6 = address6
network.IsIPv6 = "yes"
}
if udpHolePunch {
network.DefaultUDPHolePunch = "yes"
}
if localNetwork {
network.IsLocal = "yes"
}
if defaultACL {
network.DefaultACL = "yes"
}
if pointToSite {
network.IsPointToSite = "yes"
}
network.DefaultInterface = defaultInterface
network.DefaultListenPort = int32(defaultListenPort)
network.NodeLimit = int32(nodeLimit)
network.DefaultPostUp = defaultPostUp
network.DefaultPostDown = defaultPostDown
network.DefaultKeepalive = int32(defaultKeepalive)
if allowManualSignUp {
network.AllowManualSignUp = "yes"
}
network.LocalRange = localRange
network.DefaultExtClientDNS = defaultExtClientDNS
network.DefaultMTU = int32(defaultMTU)
}
functions.PrettyPrint(functions.UpdateNetwork(networkName, network))
},
}
func init() {
networkUpdateCmd.Flags().StringVar(&networkDefinitionFilePath, "file", "", "Path to network_definition.json")
networkUpdateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 address of the network")
networkUpdateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 address of the network")
networkUpdateCmd.Flags().BoolVar(&udpHolePunch, "udp_hole_punch", false, "Enable UDP Hole Punching ?")
networkUpdateCmd.Flags().BoolVar(&localNetwork, "local", false, "Is the network local (LAN) ?")
networkUpdateCmd.Flags().BoolVar(&defaultACL, "default_acl", false, "Enable default Access Control List ?")
networkUpdateCmd.Flags().BoolVar(&pointToSite, "point_to_site", false, "Enforce all clients to have only 1 central peer ?")
networkUpdateCmd.Flags().StringVar(&defaultInterface, "interface", "", "Name of the network interface")
networkUpdateCmd.Flags().StringVar(&defaultPostUp, "post_up", "", "Commands to run after server is up `;` separated")
networkUpdateCmd.Flags().StringVar(&defaultPostDown, "post_down", "", "Commands to run after server is down `;` separated")
networkUpdateCmd.Flags().StringVar(&localRange, "local_range", "", "Local CIDR range")
networkUpdateCmd.Flags().StringVar(&defaultExtClientDNS, "ext_client_dns", "", "IPv4 address of DNS server to be used by external clients")
networkUpdateCmd.Flags().IntVar(&defaultListenPort, "listen_port", 0, "Default wireguard port each node will attempt to use")
networkUpdateCmd.Flags().IntVar(&nodeLimit, "node_limit", 0, "Maximum number of nodes that can be associated with this network")
networkUpdateCmd.Flags().IntVar(&defaultKeepalive, "keep_alive", 0, "Keep Alive in seconds")
networkUpdateCmd.Flags().IntVar(&defaultMTU, "mtu", 0, "MTU size")
networkUpdateCmd.Flags().BoolVar(&allowManualSignUp, "manual_signup", false, "Allow manual signup ?")
rootCmd.AddCommand(networkUpdateCmd)
}

View File

@@ -0,0 +1,43 @@
package network_user
import (
"fmt"
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models/promodels"
"github.com/spf13/cobra"
)
var networkuserCreateCmd = &cobra.Command{
Use: "create [NETWORK NAME]",
Args: cobra.ExactArgs(1),
Short: "Create a network user",
Long: `Create a network user`,
Run: func(cmd *cobra.Command, args []string) {
user := &promodels.NetworkUser{
AccessLevel: accessLevel,
ClientLimit: clientLimit,
NodeLimit: nodeLimit, ID: promodels.NetworkUserID(id),
}
if clients != "" {
user.Clients = strings.Split(clients, ",")
}
if nodes != "" {
user.Nodes = strings.Split(nodes, ",")
}
functions.CreateNetworkUser(args[0], user)
fmt.Println("Success")
},
}
func init() {
networkuserCreateCmd.Flags().IntVar(&accessLevel, "access_level", 0, "Custom access level")
networkuserCreateCmd.Flags().IntVar(&clientLimit, "client_limit", 0, "Maximum number of external clients that can be created")
networkuserCreateCmd.Flags().IntVar(&nodeLimit, "node_limit", 999999999, "Maximum number of nodes that can be attached to a network")
networkuserCreateCmd.Flags().StringVar(&clients, "clients", "", "Access to list of external clients (comma separated)")
networkuserCreateCmd.Flags().StringVar(&nodes, "nodes", "", "Access to list of nodes (comma separated)")
networkuserCreateCmd.Flags().StringVar(&id, "id", "", "ID of the network user")
networkuserCreateCmd.MarkFlagRequired("id")
rootCmd.AddCommand(networkuserCreateCmd)
}

View File

@@ -0,0 +1,23 @@
package network_user
import (
"fmt"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var networkuserDeleteCmd = &cobra.Command{
Use: "delete [NETWORK NAME] [NETWORK USER NAME]",
Args: cobra.ExactArgs(2),
Short: "Delete a network user",
Long: `Delete a network user`,
Run: func(cmd *cobra.Command, args []string) {
functions.DeleteNetworkUser(args[0], args[1])
fmt.Println("Success")
},
}
func init() {
rootCmd.AddCommand(networkuserDeleteCmd)
}

View File

@@ -0,0 +1,10 @@
package network_user
var (
accessLevel int
clientLimit int
nodeLimit int
clients string
nodes string
id string
)

View File

@@ -0,0 +1,27 @@
package network_user
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var data bool
var networkuserGetCmd = &cobra.Command{
Use: "get [NETWORK NAME] [NETWORK USER NAME]",
Args: cobra.ExactArgs(2),
Short: "Fetch a network user",
Long: `Fetch a network user`,
Run: func(cmd *cobra.Command, args []string) {
if data {
functions.PrettyPrint(functions.GetNetworkUserData(args[1]))
} else {
functions.PrettyPrint(functions.GetNetworkUser(args[0], args[1]))
}
},
}
func init() {
networkuserGetCmd.Flags().BoolVar(&data, "data", false, "Fetch entire data of a network user")
rootCmd.AddCommand(networkuserGetCmd)
}

View File

@@ -0,0 +1,27 @@
package network_user
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var networkName string
var networkuserListCmd = &cobra.Command{
Use: "list",
Args: cobra.NoArgs,
Short: "List network users",
Long: `List network users`,
Run: func(cmd *cobra.Command, args []string) {
if networkName != "" {
functions.PrettyPrint(functions.GetNetworkUsers(networkName))
} else {
functions.PrettyPrint(functions.GetAllNetworkUsers())
}
},
}
func init() {
networkuserListCmd.Flags().StringVar(&networkName, "network", "", "Name of the network")
rootCmd.AddCommand(networkuserListCmd)
}

View File

@@ -0,0 +1,38 @@
package network_user
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "network_user",
Short: "Manage Network Users",
Long: `Manage Network Users`,
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

View File

@@ -0,0 +1,43 @@
package network_user
import (
"fmt"
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models/promodels"
"github.com/spf13/cobra"
)
var networkuserUpdateCmd = &cobra.Command{
Use: "update [NETWORK NAME]",
Args: cobra.ExactArgs(1),
Short: "Update a network user",
Long: `Update a network user`,
Run: func(cmd *cobra.Command, args []string) {
user := &promodels.NetworkUser{
AccessLevel: accessLevel,
ClientLimit: clientLimit,
NodeLimit: nodeLimit, ID: promodels.NetworkUserID(id),
}
if clients != "" {
user.Clients = strings.Split(clients, ",")
}
if nodes != "" {
user.Nodes = strings.Split(nodes, ",")
}
functions.UpdateNetworkUser(args[0], user)
fmt.Println("Success")
},
}
func init() {
networkuserUpdateCmd.Flags().IntVar(&accessLevel, "access_level", 0, "Custom access level")
networkuserUpdateCmd.Flags().IntVar(&clientLimit, "client_limit", 0, "Maximum number of external clients that can be created")
networkuserUpdateCmd.Flags().IntVar(&nodeLimit, "node_limit", 999999999, "Maximum number of nodes that can be attached to a network")
networkuserUpdateCmd.Flags().StringVar(&clients, "clients", "", "Access to list of external clients (comma separated)")
networkuserUpdateCmd.Flags().StringVar(&nodes, "nodes", "", "Access to list of nodes (comma separated)")
networkuserUpdateCmd.Flags().StringVar(&id, "id", "", "ID of the network user")
networkuserUpdateCmd.MarkFlagRequired("id")
rootCmd.AddCommand(networkuserUpdateCmd)
}

View File

@@ -0,0 +1,34 @@
package node
import (
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/spf13/cobra"
)
var nodeCreateEgressCmd = &cobra.Command{
Use: "create_egress [NETWORK NAME] [NODE ID] [EGRESS GATEWAY ADDRESSES (comma separated)]",
Args: cobra.ExactArgs(3),
Short: "Turn a Node into a Egress",
Long: `Turn a Node into a Egress`,
Run: func(cmd *cobra.Command, args []string) {
egress := &models.EgressGatewayRequest{
NetID: args[0],
NodeID: args[1],
Interface: networkInterface,
Ranges: strings.Split(args[2], ","),
}
if natEnabled {
egress.NatEnabled = "yes"
}
functions.PrettyPrint(functions.CreateEgress(args[0], args[1], egress))
},
}
func init() {
nodeCreateEgressCmd.Flags().StringVar(&networkInterface, "interface", "", "Network interface name (ex:- eth0)")
nodeCreateEgressCmd.Flags().BoolVar(&natEnabled, "nat", false, "Enable NAT for Egress Traffic ?")
rootCmd.AddCommand(nodeCreateEgressCmd)
}

View File

@@ -0,0 +1,21 @@
package node
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var nodeCreateIngressCmd = &cobra.Command{
Use: "create_ingress [NETWORK NAME] [NODE ID]",
Args: cobra.ExactArgs(2),
Short: "Turn a Node into a Ingress",
Long: `Turn a Node into a Ingress`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.CreateIngress(args[0], args[1], failover))
},
}
func init() {
nodeCreateIngressCmd.Flags().BoolVar(&failover, "failover", false, "Enable FailOver ?")
rootCmd.AddCommand(nodeCreateIngressCmd)
}

View File

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

20
cli/cmd/node/delete.go Normal file
View File

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

View File

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

View File

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

View File

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

28
cli/cmd/node/flags.go Normal file
View File

@@ -0,0 +1,28 @@
package node
var (
networkInterface string
natEnabled bool
failover bool
networkName string
nodeDefinitionFilePath string
endpoint string
listenPort int
address string
address6 string
localAddress string
name string
postUp string
postDown string
allowedIPs string
keepAlive int
relayAddrs string
egressGatewayRanges string
localRange string
mtu int
expirationDateTime int
defaultACL bool
dnsOn bool
disconnect bool
networkHub bool
)

20
cli/cmd/node/get.go Normal file
View File

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

47
cli/cmd/node/list.go Normal file
View File

@@ -0,0 +1,47 @@
package node
import (
"os"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/guumaster/tablewriter"
"github.com/spf13/cobra"
)
// nodeListCmd lists all nodes
var nodeListCmd = &cobra.Command{
Use: "list",
Args: cobra.NoArgs,
Short: "List all nodes",
Long: `List all nodes`,
Run: func(cmd *cobra.Command, args []string) {
var data []models.Node
if networkName != "" {
data = *functions.GetNodes(networkName)
} else {
data = *functions.GetNodes()
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Addresses", "Version", "Network", "Egress", "Ingress", "Relay", "ID"})
for _, d := range data {
addresses := ""
if d.Address != "" {
addresses += d.Address
}
if d.Address6 != "" {
if d.Address != "" {
addresses += ", "
}
addresses += d.Address6
}
table.Append([]string{d.Name, addresses, d.Version, d.Network, d.IsEgressGateway, d.IsIngressGateway, d.IsRelay, d.ID})
}
table.Render()
},
}
func init() {
nodeListCmd.Flags().StringVar(&networkName, "network", "", "Network name specifier")
rootCmd.AddCommand(nodeListCmd)
}

38
cli/cmd/node/root.go Normal file
View File

@@ -0,0 +1,38 @@
package node
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "node",
Short: "Manage nodes associated with a network",
Long: `Manage nodes associated with a network`,
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

22
cli/cmd/node/uncordon.go Normal file
View File

@@ -0,0 +1,22 @@
package node
import (
"fmt"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var nodeUncordonCmd = &cobra.Command{
Use: "uncordon [NETWORK NAME] [NODE ID]",
Args: cobra.ExactArgs(2),
Short: "Get a node by ID",
Long: `Get a node by ID`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(*functions.UncordonNode(args[0], args[1]))
},
}
func init() {
rootCmd.AddCommand(nodeUncordonCmd)
}

100
cli/cmd/node/update.go Normal file
View File

@@ -0,0 +1,100 @@
package node
import (
"encoding/json"
"log"
"os"
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/spf13/cobra"
)
var nodeUpdateCmd = &cobra.Command{
Use: "update [NETWORK NAME] [NODE ID]",
Args: cobra.ExactArgs(2),
Short: "Update a Node",
Long: `Update a Node`,
Run: func(cmd *cobra.Command, args []string) {
var (
node = &models.Node{}
networkName = args[0]
nodeID = args[1]
)
if nodeDefinitionFilePath != "" {
content, err := os.ReadFile(nodeDefinitionFilePath)
if err != nil {
log.Fatal("Error when opening file: ", err)
}
if err := json.Unmarshal(content, node); err != nil {
log.Fatal(err)
}
} else {
if endpoint != "" {
node.Endpoint = endpoint
node.IsStatic = "no"
}
node.ListenPort = int32(listenPort)
node.Address = address
node.Address6 = address6
node.LocalAddress = localAddress
node.Name = name
node.PostUp = postUp
node.PostDown = postDown
if allowedIPs != "" {
node.AllowedIPs = strings.Split(allowedIPs, ",")
}
node.PersistentKeepalive = int32(keepAlive)
if relayAddrs != "" {
node.RelayAddrs = strings.Split(relayAddrs, ",")
}
if egressGatewayRanges != "" {
node.EgressGatewayRanges = strings.Split(egressGatewayRanges, ",")
}
if localRange != "" {
node.LocalRange = localRange
node.IsLocal = "yes"
}
node.MTU = int32(mtu)
node.ExpirationDateTime = int64(expirationDateTime)
if defaultACL {
node.DefaultACL = "yes"
}
if dnsOn {
node.DNSOn = "yes"
}
if disconnect {
node.Connected = "no"
}
if networkHub {
node.IsHub = "yes"
}
}
functions.PrettyPrint(functions.UpdateNode(networkName, nodeID, node))
},
}
func init() {
nodeUpdateCmd.Flags().StringVar(&nodeDefinitionFilePath, "file", "", "Filepath of updated node definition in JSON")
nodeUpdateCmd.Flags().StringVar(&endpoint, "endpoint", "", "Public endpoint of the node")
nodeUpdateCmd.Flags().IntVar(&listenPort, "listen_port", 0, "Default wireguard port for the node")
nodeUpdateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 address of the node")
nodeUpdateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 address of the node")
nodeUpdateCmd.Flags().StringVar(&localAddress, "local_addr", "", "Locally reachable address of the node")
nodeUpdateCmd.Flags().StringVar(&name, "name", "", "Node name")
nodeUpdateCmd.Flags().StringVar(&postUp, "post_up", "", "Commands to run after node is up `;` separated")
nodeUpdateCmd.Flags().StringVar(&postDown, "post_down", "", "Commands to run after node is down `;` separated")
nodeUpdateCmd.Flags().StringVar(&allowedIPs, "allowed_addrs", "", "Additional private addresses given to the node (comma separated)")
nodeUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval in which packets are sent to keep connections open with peers")
nodeUpdateCmd.Flags().StringVar(&relayAddrs, "relay_addrs", "", "Addresses for relaying connections if node acts as a relay")
nodeUpdateCmd.Flags().StringVar(&egressGatewayRanges, "egress_addrs", "", "Addresses for egressing traffic if node acts as an egress")
nodeUpdateCmd.Flags().StringVar(&localRange, "local_range", "", "Local range in which the node will look for private addresses to use as an endpoint if `LocalNetwork` is enabled")
nodeUpdateCmd.Flags().IntVar(&mtu, "mtu", 0, "MTU size")
nodeUpdateCmd.Flags().IntVar(&expirationDateTime, "expiry", 0, "UNIX timestamp after which node will lose access to the network")
nodeUpdateCmd.Flags().BoolVar(&defaultACL, "acl", false, "Enable default ACL ?")
nodeUpdateCmd.Flags().BoolVar(&dnsOn, "dns", false, "Setup DNS entries for peers locally ?")
nodeUpdateCmd.Flags().BoolVar(&disconnect, "disconnect", false, "Disconnect from the network ?")
nodeUpdateCmd.Flags().BoolVar(&networkHub, "hub", false, "On a point to site network, this node is the only one which all peers connect to ?")
rootCmd.AddCommand(nodeUpdateCmd)
}

69
cli/cmd/root.go Normal file
View File

@@ -0,0 +1,69 @@
package cmd
import (
"os"
"github.com/gravitl/netmaker/cli/cmd/acl"
"github.com/gravitl/netmaker/cli/cmd/context"
"github.com/gravitl/netmaker/cli/cmd/dns"
"github.com/gravitl/netmaker/cli/cmd/ext_client"
"github.com/gravitl/netmaker/cli/cmd/keys"
"github.com/gravitl/netmaker/cli/cmd/metrics"
"github.com/gravitl/netmaker/cli/cmd/network"
"github.com/gravitl/netmaker/cli/cmd/network_user"
"github.com/gravitl/netmaker/cli/cmd/node"
"github.com/gravitl/netmaker/cli/cmd/server"
"github.com/gravitl/netmaker/cli/cmd/user"
"github.com/gravitl/netmaker/cli/cmd/usergroup"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "nmctl",
Short: "CLI for interacting with Netmaker Server",
Long: `CLI for interacting with Netmaker Server`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root of all subcommands
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.tctl.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
// IMP: Bind subcommands here
rootCmd.AddCommand(network.GetRoot())
rootCmd.AddCommand(context.GetRoot())
rootCmd.AddCommand(keys.GetRoot())
rootCmd.AddCommand(acl.GetRoot())
rootCmd.AddCommand(node.GetRoot())
rootCmd.AddCommand(dns.GetRoot())
rootCmd.AddCommand(server.GetRoot())
rootCmd.AddCommand(ext_client.GetRoot())
rootCmd.AddCommand(user.GetRoot())
rootCmd.AddCommand(usergroup.GetRoot())
rootCmd.AddCommand(metrics.GetRoot())
rootCmd.AddCommand(network_user.GetRoot())
}

20
cli/cmd/server/config.go Normal file
View File

@@ -0,0 +1,20 @@
package server
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var serverConfigCmd = &cobra.Command{
Use: "config",
Args: cobra.NoArgs,
Short: "Retrieve server config",
Long: `Retrieve server config`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.GetServerConfig())
},
}
func init() {
rootCmd.AddCommand(serverConfigCmd)
}

View File

@@ -0,0 +1,22 @@
package server
import (
"fmt"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var serverHasAdminCmd = &cobra.Command{
Use: "has_admin",
Args: cobra.NoArgs,
Short: "Check if server has an admin",
Long: `Check if server has an admin`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(*functions.HasAdmin())
},
}
func init() {
rootCmd.AddCommand(serverHasAdminCmd)
}

20
cli/cmd/server/health.go Normal file
View File

@@ -0,0 +1,20 @@
package server
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var serverHealthCmd = &cobra.Command{
Use: "health",
Args: cobra.NoArgs,
Short: "View server health",
Long: `View server health`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.GetServerHealth())
},
}
func init() {
rootCmd.AddCommand(serverHealthCmd)
}

20
cli/cmd/server/info.go Normal file
View File

@@ -0,0 +1,20 @@
package server
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var serverInfoCmd = &cobra.Command{
Use: "info",
Args: cobra.NoArgs,
Short: "Retrieve server information",
Long: `Retrieve server information`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.GetServerInfo())
},
}
func init() {
rootCmd.AddCommand(serverInfoCmd)
}

38
cli/cmd/server/root.go Normal file
View File

@@ -0,0 +1,38 @@
package server
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "server",
Short: "Get netmaker server information",
Long: `Get netmaker server information`,
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

37
cli/cmd/user/create.go Normal file
View File

@@ -0,0 +1,37 @@
package user
import (
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/spf13/cobra"
)
var userCreateCmd = &cobra.Command{
Use: "create",
Args: cobra.NoArgs,
Short: "Create a new user",
Long: `Create a new user`,
Run: func(cmd *cobra.Command, args []string) {
user := &models.User{UserName: username, Password: password, IsAdmin: admin}
if networks != "" {
user.Networks = strings.Split(networks, ",")
}
if groups != "" {
user.Groups = strings.Split(groups, ",")
}
functions.PrettyPrint(functions.CreateUser(user))
},
}
func init() {
userCreateCmd.Flags().StringVar(&username, "name", "", "Name of the user")
userCreateCmd.Flags().StringVar(&password, "password", "", "Password of the user")
userCreateCmd.MarkFlagRequired("name")
userCreateCmd.MarkFlagRequired("password")
userCreateCmd.Flags().BoolVar(&admin, "admin", false, "Make the user an admin ?")
userCreateCmd.Flags().StringVar(&networks, "networks", "", "List of networks the user will access to (comma separated)")
userCreateCmd.Flags().StringVar(&groups, "groups", "", "List of user groups the user will be part of (comma separated)")
rootCmd.AddCommand(userCreateCmd)
}

20
cli/cmd/user/delete.go Normal file
View File

@@ -0,0 +1,20 @@
package user
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var userDeleteCmd = &cobra.Command{
Use: "delete [USER NAME]",
Args: cobra.ExactArgs(1),
Short: "Delete a user",
Long: `Delete a user`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(*functions.DeleteUser(args[0]))
},
}
func init() {
rootCmd.AddCommand(userDeleteCmd)
}

9
cli/cmd/user/flags.go Normal file
View File

@@ -0,0 +1,9 @@
package user
var (
username string
password string
admin bool
networks string
groups string
)

20
cli/cmd/user/get.go Normal file
View File

@@ -0,0 +1,20 @@
package user
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var userGetCmd = &cobra.Command{
Use: "get [USER NAME]",
Args: cobra.ExactArgs(1),
Short: "Get a user",
Long: `Get a user`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.GetUser(args[0]))
},
}
func init() {
rootCmd.AddCommand(userGetCmd)
}

30
cli/cmd/user/list.go Normal file
View File

@@ -0,0 +1,30 @@
package user
import (
"os"
"strconv"
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/guumaster/tablewriter"
"github.com/spf13/cobra"
)
var userListCmd = &cobra.Command{
Use: "list",
Args: cobra.NoArgs,
Short: "List all users",
Long: `List all users`,
Run: func(cmd *cobra.Command, args []string) {
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Admin", "Networks", "Groups"})
for _, d := range *functions.ListUsers() {
table.Append([]string{d.UserName, strconv.FormatBool(d.IsAdmin), strings.Join(d.Networks, ", "), strings.Join(d.Groups, ", ")})
}
table.Render()
},
}
func init() {
rootCmd.AddCommand(userListCmd)
}

38
cli/cmd/user/root.go Normal file
View File

@@ -0,0 +1,38 @@
package user
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "user",
Short: "Manage users and permissions",
Long: `Manage users and permissions`,
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

35
cli/cmd/user/update.go Normal file
View File

@@ -0,0 +1,35 @@
package user
import (
"strings"
"github.com/gravitl/netmaker/cli/functions"
"github.com/gravitl/netmaker/models"
"github.com/spf13/cobra"
)
var userUpdateCmd = &cobra.Command{
Use: "update [USER NAME]",
Args: cobra.ExactArgs(1),
Short: "Update a user",
Long: `Update a user`,
Run: func(cmd *cobra.Command, args []string) {
user := &models.User{UserName: args[0], IsAdmin: admin}
if networks != "" {
user.Networks = strings.Split(networks, ",")
}
if groups != "" {
user.Groups = strings.Split(groups, ",")
} else {
user.Groups = []string{"*"}
}
functions.PrettyPrint(functions.UpdateUser(user))
},
}
func init() {
userUpdateCmd.Flags().BoolVar(&admin, "admin", false, "Make the user an admin ?")
userUpdateCmd.Flags().StringVar(&networks, "networks", "", "List of networks the user will access to (comma separated)")
userUpdateCmd.Flags().StringVar(&groups, "groups", "", "List of user groups the user will be part of (comma separated)")
rootCmd.AddCommand(userUpdateCmd)
}

View File

@@ -0,0 +1,23 @@
package usergroup
import (
"fmt"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var usergroupCreateCmd = &cobra.Command{
Use: "create [GROUP NAME]",
Args: cobra.ExactArgs(1),
Short: "Create a usergroup",
Long: `Create a usergroup`,
Run: func(cmd *cobra.Command, args []string) {
functions.CreateUsergroup(args[0])
fmt.Println("Success")
},
}
func init() {
rootCmd.AddCommand(usergroupCreateCmd)
}

View File

@@ -0,0 +1,23 @@
package usergroup
import (
"fmt"
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var usergroupDeleteCmd = &cobra.Command{
Use: "delete [GROUP NAME]",
Args: cobra.ExactArgs(1),
Short: "Delete a usergroup",
Long: `Delete a usergroup`,
Run: func(cmd *cobra.Command, args []string) {
functions.DeleteUsergroup(args[0])
fmt.Println("Success")
},
}
func init() {
rootCmd.AddCommand(usergroupDeleteCmd)
}

20
cli/cmd/usergroup/get.go Normal file
View File

@@ -0,0 +1,20 @@
package usergroup
import (
"github.com/gravitl/netmaker/cli/functions"
"github.com/spf13/cobra"
)
var usergroupGetCmd = &cobra.Command{
Use: "get",
Args: cobra.NoArgs,
Short: "Fetch all usergroups",
Long: `Fetch all usergroups`,
Run: func(cmd *cobra.Command, args []string) {
functions.PrettyPrint(functions.GetUsergroups())
},
}
func init() {
rootCmd.AddCommand(usergroupGetCmd)
}

38
cli/cmd/usergroup/root.go Normal file
View File

@@ -0,0 +1,38 @@
package usergroup
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "usergroup",
Short: "Manage User Groups",
Long: `Manage User Groups`,
// Run: func(cmd *cobra.Command, args []string) { },
}
// GetRoot returns the root subcommand
func GetRoot() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

145
cli/config/config.go Normal file
View File

@@ -0,0 +1,145 @@
package config
import (
"fmt"
"log"
"os"
"path/filepath"
"gopkg.in/yaml.v3"
)
// Context maintains configuration for interaction with Netmaker API
type Context struct {
Endpoint string `yaml:"endpoint"`
Username string `yaml:"username,omitempty"`
Password string `yaml:"password,omitempty"`
MasterKey string `yaml:"masterkey,omitempty"`
Current bool `yaml:"current,omitempty"`
AuthToken string `yaml:"auth_token,omitempty"`
SSO bool `yaml:"sso,omitempty"`
}
var (
contextMap = map[string]Context{}
configFilePath string
filename string
)
func createConfigPathIfNotExists() {
homeDir, err := os.UserHomeDir()
if err != nil {
log.Fatal(err)
}
configFilePath = filepath.Join(homeDir, ".netmaker")
// create directory if not exists
if err := os.MkdirAll(configFilePath, os.ModePerm); err != nil {
log.Fatal(err)
}
filename = filepath.Join(configFilePath, "config.yml")
// create file if not exists
if _, err := os.Stat(filename); err != nil {
if os.IsNotExist(err) {
if _, err := os.Create(filename); err != nil {
log.Fatalf("Unable to create file filename: %s", err)
}
} else {
log.Fatal(err)
}
}
}
func loadConfig() {
content, err := os.ReadFile(filename)
if err != nil {
log.Fatalf("Error reading config file: %s", err)
}
if err := yaml.Unmarshal(content, &contextMap); err != nil {
log.Fatalf("Unable to decode YAML into struct: %s", err)
}
}
func saveContext() {
bodyBytes, err := yaml.Marshal(&contextMap)
if err != nil {
log.Fatalf("Error marshalling into YAML %s", err)
}
file, err := os.Create(filename)
if err != nil {
log.Fatal(err)
}
if _, err := file.Write(bodyBytes); err != nil {
log.Fatal(err)
}
if err := file.Close(); err != nil {
log.Fatal(err)
}
}
// GetCurrentContext - returns current set context
func GetCurrentContext() (name string, ctx Context) {
for n, c := range contextMap {
if c.Current {
name, ctx = n, c
return
}
}
log.Fatalf("No current context set, do so via `netmaker context use <name>`")
return
}
// SetCurrentContext - sets a given context as current context
func SetCurrentContext(ctxName string) {
if _, ok := contextMap[ctxName]; !ok {
log.Fatalf("No such context %s", ctxName)
}
for key, ctx := range contextMap {
ctx.Current = key == ctxName
contextMap[key] = ctx
}
saveContext()
}
// SetContext - updates an existing context or creates a new one
func SetContext(ctxName string, ctx Context) {
if oldCtx, ok := contextMap[ctxName]; ok && oldCtx.Current {
ctx.Current = true
}
contextMap[ctxName] = ctx
saveContext()
}
// SetAuthToken - saves the auth token
func SetAuthToken(authToken string) {
ctxName, _ := GetCurrentContext()
if ctx, ok := contextMap[ctxName]; ok {
ctx.AuthToken = authToken
contextMap[ctxName] = ctx
saveContext()
}
}
// DeleteContext - deletes a context
func DeleteContext(ctxName string) {
if _, ok := contextMap[ctxName]; ok {
delete(contextMap, ctxName)
saveContext()
} else {
log.Fatalf("No such context %s", ctxName)
}
}
// ListAll - lists all contexts
func ListAll() {
for key, ctx := range contextMap {
fmt.Print("\n", key, " -> ", ctx.Endpoint)
if ctx.Current {
fmt.Print(" (current)")
}
}
}
func init() {
createConfigPathIfNotExists()
loadConfig()
}

18
cli/functions/acl.go Normal file
View File

@@ -0,0 +1,18 @@
package functions
import (
"fmt"
"net/http"
"github.com/gravitl/netmaker/logic/acls"
)
// GetACL - fetch all ACLs associated with a network
func GetACL(networkName string) *acls.ACLContainer {
return request[acls.ACLContainer](http.MethodGet, fmt.Sprintf("/api/networks/%s/acls", networkName), nil)
}
// UpdateACL - update an ACL
func UpdateACL(networkName string, payload *acls.ACLContainer) *acls.ACLContainer {
return request[acls.ACLContainer](http.MethodPut, fmt.Sprintf("/api/networks/%s/acls", networkName), payload)
}

43
cli/functions/dns.go Normal file
View File

@@ -0,0 +1,43 @@
package functions
import (
"fmt"
"net/http"
"github.com/gravitl/netmaker/models"
)
// GetDNS - fetch all DNS entries
func GetDNS() *[]models.DNSEntry {
return request[[]models.DNSEntry](http.MethodGet, "/api/dns", nil)
}
// GetNodeDNS - fetch all Node DNS entires
func GetNodeDNS(networkName string) *[]models.DNSEntry {
return request[[]models.DNSEntry](http.MethodGet, fmt.Sprintf("/api/dns/adm/%s/nodes", networkName), nil)
}
// GetCustomDNS - fetch user defined DNS entriees
func GetCustomDNS(networkName string) *[]models.DNSEntry {
return request[[]models.DNSEntry](http.MethodGet, fmt.Sprintf("/api/dns/adm/%s/custom", networkName), nil)
}
// GetNetworkDNS - fetch DNS entries associated with a network
func GetNetworkDNS(networkName string) *[]models.DNSEntry {
return request[[]models.DNSEntry](http.MethodGet, "/api/dns/adm/"+networkName, nil)
}
// CreateDNS - create a DNS entry
func CreateDNS(networkName string, payload *models.DNSEntry) *models.DNSEntry {
return request[models.DNSEntry](http.MethodPost, "/api/dns/"+networkName, payload)
}
// PushDNS - push a DNS entry to CoreDNS
func PushDNS() *string {
return request[string](http.MethodPost, "/api/dns/adm/pushdns", nil)
}
// DeleteDNS - delete a DNS entry
func DeleteDNS(networkName, domainName string) *string {
return request[string](http.MethodDelete, fmt.Sprintf("/api/dns/%s/%s", networkName, domainName), nil)
}

View File

@@ -0,0 +1,49 @@
package functions
import (
"fmt"
"net/http"
"github.com/gravitl/netmaker/models"
)
// GetAllExtClients - fetch all external clients
func GetAllExtClients() *[]models.ExtClient {
return request[[]models.ExtClient](http.MethodGet, "/api/extclients", nil)
}
// GetNetworkExtClients - fetch external clients associated with a network
func GetNetworkExtClients(networkName string) *[]models.ExtClient {
return request[[]models.ExtClient](http.MethodGet, "/api/extclients/"+networkName, nil)
}
// GetExtClient - fetch a single external client
func GetExtClient(networkName, clientID string) *models.ExtClient {
return request[models.ExtClient](http.MethodGet, fmt.Sprintf("/api/extclients/%s/%s", networkName, clientID), nil)
}
// GetExtClientConfig - fetch a wireguard config of an external client
func GetExtClientConfig(networkName, clientID string) string {
return get(fmt.Sprintf("/api/extclients/%s/%s/file", networkName, clientID))
}
// CreateExtClient - create an external client
func CreateExtClient(networkName, nodeID, extClientID string) {
if extClientID != "" {
request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), &models.CustomExtClient{
ClientID: extClientID,
})
} else {
request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), nil)
}
}
// DeleteExtClient - delete an external client
func DeleteExtClient(networkName, clientID string) *models.SuccessResponse {
return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/extclients/%s/%s", networkName, clientID), nil)
}
// UpdateExtClient - update an external client
func UpdateExtClient(networkName, clientID string, payload *models.ExtClient) *models.ExtClient {
return request[models.ExtClient](http.MethodPut, fmt.Sprintf("/api/extclients/%s/%s", networkName, clientID), payload)
}

View File

@@ -0,0 +1,189 @@
package functions
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"os/signal"
"strings"
"github.com/gorilla/websocket"
"github.com/gravitl/netmaker/cli/config"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
)
func ssoLogin(endpoint string) string {
var (
authToken string
interrupt = make(chan os.Signal, 1)
url, _ = url.Parse(endpoint)
socketURL = fmt.Sprintf("wss://%s/api/oauth/headless", url.Host)
)
signal.Notify(interrupt, os.Interrupt)
conn, _, err := websocket.DefaultDialer.Dial(socketURL, nil)
if err != nil {
log.Fatal("error connecting to endpoint ", socketURL, err.Error())
}
defer conn.Close()
_, msg, err := conn.ReadMessage()
if err != nil {
log.Fatal("error reading from server: ", err.Error())
}
fmt.Printf("Please visit:\n %s \n to authenticate\n", string(msg))
done := make(chan struct{})
defer close(done)
go func() {
for {
msgType, msg, err := conn.ReadMessage()
if err != nil {
if msgType < 0 {
done <- struct{}{}
return
}
if !strings.Contains(err.Error(), "normal") {
log.Fatal("read error: ", err.Error())
}
return
}
if msgType == websocket.CloseMessage {
done <- struct{}{}
return
}
if strings.Contains(string(msg), "JWT: ") {
authToken = strings.TrimPrefix(string(msg), "JWT: ")
} else {
logger.Log(0, "Message from server:", string(msg))
return
}
}
}()
for {
select {
case <-done:
return authToken
case <-interrupt:
err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
logger.Log(0, "write close:", err.Error())
}
return authToken
}
}
}
func getAuthToken(ctx config.Context, force bool) string {
if !force && ctx.AuthToken != "" {
return ctx.AuthToken
}
if ctx.SSO {
authToken := ssoLogin(ctx.Endpoint)
config.SetAuthToken(authToken)
return authToken
}
authParams := &models.UserAuthParams{UserName: ctx.Username, Password: ctx.Password}
payload, err := json.Marshal(authParams)
if err != nil {
log.Fatal(err)
}
res, err := http.Post(ctx.Endpoint+"/api/users/adm/authenticate", "application/json", bytes.NewReader(payload))
if err != nil {
log.Fatal(err)
}
resBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
log.Fatalf("Client could not read response body: %s", err)
}
if res.StatusCode != http.StatusOK {
log.Fatalf("Error Status: %d Response: %s", res.StatusCode, string(resBodyBytes))
}
body := new(models.SuccessResponse)
if err := json.Unmarshal(resBodyBytes, body); err != nil {
log.Fatalf("Error unmarshalling JSON: %s", err)
}
authToken := body.Response.(map[string]any)["AuthToken"].(string)
config.SetAuthToken(authToken)
return authToken
}
func request[T any](method, route string, payload any) *T {
var (
_, ctx = config.GetCurrentContext()
req *http.Request
err error
)
if payload == nil {
req, err = http.NewRequest(method, ctx.Endpoint+route, nil)
if err != nil {
log.Fatalf("Client could not create request: %s", err)
}
} else {
payloadBytes, jsonErr := json.Marshal(payload)
if jsonErr != nil {
log.Fatalf("Error in request JSON marshalling: %s", err)
}
req, err = http.NewRequest(method, ctx.Endpoint+route, bytes.NewReader(payloadBytes))
if err != nil {
log.Fatalf("Client could not create request: %s", err)
}
req.Header.Set("Content-Type", "application/json")
}
if ctx.MasterKey != "" {
req.Header.Set("Authorization", "Bearer "+ctx.MasterKey)
} else {
req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, false))
}
retried := false
retry:
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatalf("Client error making http request: %s", err)
}
// refresh JWT token
if res.StatusCode == http.StatusUnauthorized && !retried && ctx.MasterKey == "" {
req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, true))
retried = true
goto retry
}
resBodyBytes, err := io.ReadAll(res.Body)
if err != nil {
log.Fatalf("Client could not read response body: %s", err)
}
if res.StatusCode != http.StatusOK {
log.Fatalf("Error Status: %d Response: %s", res.StatusCode, string(resBodyBytes))
}
body := new(T)
if len(resBodyBytes) > 0 {
if err := json.Unmarshal(resBodyBytes, body); err != nil {
log.Fatalf("Error unmarshalling JSON: %s", err)
}
}
return body
}
func get(route string) string {
_, ctx := config.GetCurrentContext()
req, err := http.NewRequest(http.MethodGet, ctx.Endpoint+route, nil)
if err != nil {
log.Fatal(err)
}
if ctx.MasterKey != "" {
req.Header.Set("Authorization", "Bearer "+ctx.MasterKey)
} else {
req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, true))
}
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
bodyBytes, err := io.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
return string(bodyBytes)
}

23
cli/functions/keys.go Normal file
View File

@@ -0,0 +1,23 @@
package functions
import (
"fmt"
"net/http"
"github.com/gravitl/netmaker/models"
)
// GetKeys - fetch all access keys of a network
func GetKeys(networkName string) *[]models.AccessKey {
return request[[]models.AccessKey](http.MethodGet, fmt.Sprintf("/api/networks/%s/keys", networkName), nil)
}
// CreateKey - create an access key
func CreateKey(networkName string, key *models.AccessKey) *models.AccessKey {
return request[models.AccessKey](http.MethodPost, fmt.Sprintf("/api/networks/%s/keys", networkName), key)
}
// DeleteKey - delete an access key
func DeleteKey(networkName, keyName string) {
request[string](http.MethodDelete, fmt.Sprintf("/api/networks/%s/keys/%s", networkName, keyName), nil)
}

Some files were not shown because too many files have changed in this diff Show More