mirror of
https://github.com/gravitl/netmaker.git
synced 2024-09-20 15:26:04 +08:00
commit
5693384ec3
1
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
|
@ -31,6 +31,7 @@ body:
|
|||
label: Version
|
||||
description: What version are you running?
|
||||
options:
|
||||
- v0.24.0
|
||||
- v0.23.0
|
||||
- v0.22.0
|
||||
- v0.21.2
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -25,3 +25,4 @@ data/
|
|||
netmaker.exe
|
||||
netmaker.code-workspace
|
||||
dist/
|
||||
nmctl
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<p align="center">
|
||||
<a href="https://github.com/gravitl/netmaker/releases">
|
||||
<img src="https://img.shields.io/badge/Version-0.23.0-informational?style=flat-square" />
|
||||
<img src="https://img.shields.io/badge/Version-0.24.0-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" />
|
||||
|
|
51
auth/auth.go
51
auth/auth.go
|
@ -32,7 +32,6 @@ const (
|
|||
github_provider_name = "github"
|
||||
oidc_provider_name = "oidc"
|
||||
verify_user = "verifyuser"
|
||||
auth_key = "netmaker_auth"
|
||||
user_signin_length = 16
|
||||
node_signin_length = 64
|
||||
headless_signin_length = 32
|
||||
|
@ -75,10 +74,10 @@ func InitializeAuthProvider() string {
|
|||
if functions == nil {
|
||||
return ""
|
||||
}
|
||||
var _, err = fetchPassValue(logic.RandomString(64))
|
||||
logger.Log(0, "setting oauth secret")
|
||||
var err = logic.SetAuthSecret(logic.RandomString(64))
|
||||
if err != nil {
|
||||
logger.Log(0, err.Error())
|
||||
return ""
|
||||
logger.FatalLog("failed to set auth_secret", err.Error())
|
||||
}
|
||||
var authInfo = servercfg.GetAuthProviderInfo()
|
||||
var serverConn = servercfg.GetAPIHost()
|
||||
|
@ -156,7 +155,7 @@ func HandleAuthLogin(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// IsOauthUser - returns
|
||||
func IsOauthUser(user *models.User) error {
|
||||
var currentValue, err = fetchPassValue("")
|
||||
var currentValue, err = FetchPassValue("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -246,8 +245,9 @@ func addUser(email string) error {
|
|||
slog.Error("error checking for existence of admin user during OAuth login for", "email", email, "error", err)
|
||||
return err
|
||||
} // generate random password to adapt to current model
|
||||
var newPass, fetchErr = fetchPassValue("")
|
||||
var newPass, fetchErr = FetchPassValue("")
|
||||
if fetchErr != nil {
|
||||
slog.Error("failed to get password", "error", err.Error())
|
||||
return fetchErr
|
||||
}
|
||||
var newUser = models.User{
|
||||
|
@ -255,6 +255,7 @@ func addUser(email string) error {
|
|||
Password: newPass,
|
||||
}
|
||||
if !hasSuperAdmin { // must be first attempt, create a superadmin
|
||||
logger.Log(0, "creating superadmin")
|
||||
if err = logic.CreateSuperAdmin(&newUser); err != nil {
|
||||
slog.Error("error creating super admin from user", "email", email, "error", err)
|
||||
} else {
|
||||
|
@ -264,7 +265,7 @@ func addUser(email string) error {
|
|||
// TODO: add ability to add users with preemptive permissions
|
||||
newUser.IsAdmin = false
|
||||
if err = logic.CreateUser(&newUser); err != nil {
|
||||
logger.Log(1, "error creating user,", email, "; user not added")
|
||||
logger.Log(0, "error creating user,", email, "; user not added", "error", err.Error())
|
||||
} else {
|
||||
logger.Log(0, "user created from ", email)
|
||||
}
|
||||
|
@ -272,25 +273,17 @@ func addUser(email string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func fetchPassValue(newValue string) (string, error) {
|
||||
func FetchPassValue(newValue string) (string, error) {
|
||||
|
||||
type valueHolder struct {
|
||||
Value string `json:"value" bson:"value"`
|
||||
}
|
||||
var b64NewValue = base64.StdEncoding.EncodeToString([]byte(newValue))
|
||||
var newValueHolder = &valueHolder{
|
||||
Value: b64NewValue,
|
||||
}
|
||||
var data, marshalErr = json.Marshal(newValueHolder)
|
||||
if marshalErr != nil {
|
||||
return "", marshalErr
|
||||
}
|
||||
|
||||
var currentValue, err = logic.FetchAuthSecret(auth_key, string(data))
|
||||
newValueHolder := valueHolder{}
|
||||
var currentValue, err = logic.FetchAuthSecret()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var unmarshErr = json.Unmarshal([]byte(currentValue), newValueHolder)
|
||||
var unmarshErr = json.Unmarshal([]byte(currentValue), &newValueHolder)
|
||||
if unmarshErr != nil {
|
||||
return "", unmarshErr
|
||||
}
|
||||
|
@ -334,3 +327,23 @@ func isStateCached(state string) bool {
|
|||
_, err := netcache.Get(state)
|
||||
return err == nil || strings.Contains(err.Error(), "expired")
|
||||
}
|
||||
|
||||
// isEmailAllowed - checks if email is allowed to signup
|
||||
func isEmailAllowed(email string) bool {
|
||||
allowedDomains := servercfg.GetAllowedEmailDomains()
|
||||
domains := strings.Split(allowedDomains, ",")
|
||||
if len(domains) == 1 && domains[0] == "*" {
|
||||
return true
|
||||
}
|
||||
emailParts := strings.Split(email, "@")
|
||||
if len(emailParts) < 2 {
|
||||
return false
|
||||
}
|
||||
baseDomainOfEmail := emailParts[1]
|
||||
for _, domain := range domains {
|
||||
if domain == baseDomainOfEmail {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/gravitl/netmaker/database"
|
||||
"github.com/gravitl/netmaker/logger"
|
||||
"github.com/gravitl/netmaker/logic"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
|
@ -60,9 +61,29 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
|
|||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
if !isEmailAllowed(content.UserPrincipalName) {
|
||||
handleOauthUserNotAllowedToSignUp(w)
|
||||
return
|
||||
}
|
||||
// check if user approval is already pending
|
||||
if logic.IsPendingUser(content.UserPrincipalName) {
|
||||
handleOauthUserSignUpApprovalPending(w)
|
||||
return
|
||||
}
|
||||
_, err = logic.GetUser(content.UserPrincipalName)
|
||||
if err != nil { // user must not exists, so try to make one
|
||||
if err = addUser(content.UserPrincipalName); err != nil {
|
||||
if err != nil {
|
||||
if database.IsEmptyRecord(err) { // user must not exist, so try to make one
|
||||
err = logic.InsertPendingUser(&models.User{
|
||||
UserName: content.UserPrincipalName,
|
||||
})
|
||||
if err != nil {
|
||||
handleSomethingWentWrong(w)
|
||||
return
|
||||
}
|
||||
handleFirstTimeOauthUserSignUp(w)
|
||||
return
|
||||
} else {
|
||||
handleSomethingWentWrong(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +96,7 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
|
|||
handleOauthUserNotAllowed(w)
|
||||
return
|
||||
}
|
||||
var newPass, fetchErr = fetchPassValue("")
|
||||
var newPass, fetchErr = FetchPassValue("")
|
||||
if fetchErr != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -12,17 +12,44 @@ const oauthNotConfigured = `<!DOCTYPE html><html>
|
|||
|
||||
const userNotAllowed = `<!DOCTYPE html><html>
|
||||
<body>
|
||||
<h3>Only Admins are allowed to access Dashboard.</h3>
|
||||
<h3>Only administrators can access the Dashboard. Please contact your administrator to elevate your account.</h3>
|
||||
<p>Non-Admins can access the netmaker networks using <a href="https://docs.netmaker.io/pro/rac.html" target="_blank" rel="noopener">RemoteAccessClient.</a></p>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
const userFirstTimeSignUp = `<!DOCTYPE html><html>
|
||||
<body>
|
||||
<h3>Thank you for signing up. Please contact your administrator for access.</h3>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
const userSignUpApprovalPending = `<!DOCTYPE html><html>
|
||||
<body>
|
||||
<h3>Your account is yet to be approved. Please contact your administrator for access.</h3>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
const userNotFound = `<!DOCTYPE html><html>
|
||||
<body>
|
||||
<h3>User Not Found.</h3>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
const somethingwentwrong = `<!DOCTYPE html><html>
|
||||
<body>
|
||||
<h3>Something went wrong. Contact Admin.</h3>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
const notallowedtosignup = `<!DOCTYPE html><html>
|
||||
<body>
|
||||
<h3>Your email is not allowed. Please contact your administrator.</h3>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
func handleOauthUserNotFound(response http.ResponseWriter) {
|
||||
response.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
response.WriteHeader(http.StatusNotFound)
|
||||
|
@ -34,6 +61,23 @@ func handleOauthUserNotAllowed(response http.ResponseWriter) {
|
|||
response.WriteHeader(http.StatusForbidden)
|
||||
response.Write([]byte(userNotAllowed))
|
||||
}
|
||||
func handleFirstTimeOauthUserSignUp(response http.ResponseWriter) {
|
||||
response.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
response.WriteHeader(http.StatusForbidden)
|
||||
response.Write([]byte(userFirstTimeSignUp))
|
||||
}
|
||||
|
||||
func handleOauthUserSignUpApprovalPending(response http.ResponseWriter) {
|
||||
response.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
response.WriteHeader(http.StatusForbidden)
|
||||
response.Write([]byte(userSignUpApprovalPending))
|
||||
}
|
||||
|
||||
func handleOauthUserNotAllowedToSignUp(response http.ResponseWriter) {
|
||||
response.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
response.WriteHeader(http.StatusForbidden)
|
||||
response.Write([]byte(notallowedtosignup))
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
@ -41,3 +85,9 @@ func handleOauthNotConfigured(response http.ResponseWriter) {
|
|||
response.WriteHeader(http.StatusInternalServerError)
|
||||
response.Write([]byte(oauthNotConfigured))
|
||||
}
|
||||
|
||||
func handleSomethingWentWrong(response http.ResponseWriter) {
|
||||
response.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
response.WriteHeader(http.StatusInternalServerError)
|
||||
response.Write([]byte(somethingwentwrong))
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/gravitl/netmaker/database"
|
||||
"github.com/gravitl/netmaker/logger"
|
||||
"github.com/gravitl/netmaker/logic"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
|
@ -60,9 +61,29 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
|
|||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
if !isEmailAllowed(content.Login) {
|
||||
handleOauthUserNotAllowedToSignUp(w)
|
||||
return
|
||||
}
|
||||
// check if user approval is already pending
|
||||
if logic.IsPendingUser(content.Login) {
|
||||
handleOauthUserSignUpApprovalPending(w)
|
||||
return
|
||||
}
|
||||
_, err = logic.GetUser(content.Login)
|
||||
if err != nil { // user must not exist, so try to make one
|
||||
if err = addUser(content.Login); err != nil {
|
||||
if err != nil {
|
||||
if database.IsEmptyRecord(err) { // user must not exist, so try to make one
|
||||
err = logic.InsertPendingUser(&models.User{
|
||||
UserName: content.Login,
|
||||
})
|
||||
if err != nil {
|
||||
handleSomethingWentWrong(w)
|
||||
return
|
||||
}
|
||||
handleFirstTimeOauthUserSignUp(w)
|
||||
return
|
||||
} else {
|
||||
handleSomethingWentWrong(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +96,7 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
|
|||
handleOauthUserNotAllowed(w)
|
||||
return
|
||||
}
|
||||
var newPass, fetchErr = fetchPassValue("")
|
||||
var newPass, fetchErr = FetchPassValue("")
|
||||
if fetchErr != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gravitl/netmaker/database"
|
||||
"github.com/gravitl/netmaker/logger"
|
||||
"github.com/gravitl/netmaker/logic"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
|
@ -62,14 +63,35 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
|
|||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
if !isEmailAllowed(content.Email) {
|
||||
handleOauthUserNotAllowedToSignUp(w)
|
||||
return
|
||||
}
|
||||
// check if user approval is already pending
|
||||
if logic.IsPendingUser(content.Email) {
|
||||
handleOauthUserSignUpApprovalPending(w)
|
||||
return
|
||||
}
|
||||
_, err = logic.GetUser(content.Email)
|
||||
if err != nil { // user must not exists, so try to make one
|
||||
if err = addUser(content.Email); err != nil {
|
||||
if err != nil {
|
||||
if database.IsEmptyRecord(err) { // user must not exist, so try to make one
|
||||
err = logic.InsertPendingUser(&models.User{
|
||||
UserName: content.Email,
|
||||
})
|
||||
if err != nil {
|
||||
handleSomethingWentWrong(w)
|
||||
return
|
||||
}
|
||||
handleFirstTimeOauthUserSignUp(w)
|
||||
return
|
||||
} else {
|
||||
handleSomethingWentWrong(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
user, err := logic.GetUser(content.Email)
|
||||
if err != nil {
|
||||
logger.Log(0, "error fetching user: ", err.Error())
|
||||
handleOauthUserNotFound(w)
|
||||
return
|
||||
}
|
||||
|
@ -77,7 +99,7 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
|
|||
handleOauthUserNotAllowed(w)
|
||||
return
|
||||
}
|
||||
var newPass, fetchErr = fetchPassValue("")
|
||||
var newPass, fetchErr = FetchPassValue("")
|
||||
if fetchErr != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -50,19 +50,24 @@ func HandleHeadlessSSOCallback(w http.ResponseWriter, r *http.Request) {
|
|||
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())
|
||||
// check if user approval is already pending
|
||||
if logic.IsPendingUser(userClaims.getUserName()) {
|
||||
handleOauthUserNotAllowed(w)
|
||||
return
|
||||
}
|
||||
user, err := logic.GetUser(userClaims.getUserName())
|
||||
if err != nil {
|
||||
response := returnErrTemplate("", "user not found", state, reqKeyIf)
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
w.Write(response)
|
||||
return
|
||||
}
|
||||
newPass, fetchErr := fetchPassValue("")
|
||||
newPass, fetchErr := FetchPassValue("")
|
||||
if fetchErr != nil {
|
||||
return
|
||||
}
|
||||
jwt, jwtErr := logic.VerifyAuthRequest(models.UserAuthParams{
|
||||
UserName: userClaims.getUserName(),
|
||||
UserName: user.UserName,
|
||||
Password: newPass,
|
||||
})
|
||||
if jwtErr != nil {
|
||||
|
|
|
@ -248,12 +248,20 @@ func CheckNetRegAndHostUpdate(networks []string, h *models.Host, relayNodeId uui
|
|||
// check if relay node exists and acting as relay
|
||||
relaynode, err := logic.GetNodeByID(relayNodeId.String())
|
||||
if err == nil && relaynode.IsRelay {
|
||||
slog.Info(fmt.Sprintf("adding relayed node %s to relay %s on network %s", newNode.ID.String(), relayNodeId.String(), network))
|
||||
newNode.IsRelayed = true
|
||||
newNode.RelayedBy = relayNodeId.String()
|
||||
slog.Info(fmt.Sprintf("adding relayed node %s to relay %s on network %s", newNode.ID.String(), relayNodeId.String(), network))
|
||||
updatedRelayNode := relaynode
|
||||
updatedRelayNode.RelayedNodes = append(updatedRelayNode.RelayedNodes, newNode.ID.String())
|
||||
logic.UpdateRelayed(&relaynode, &updatedRelayNode)
|
||||
if err := logic.UpsertNode(&updatedRelayNode); err != nil {
|
||||
slog.Error("failed to update node", "nodeid", relayNodeId.String())
|
||||
}
|
||||
if err := logic.UpsertNode(newNode); err != nil {
|
||||
slog.Error("failed to update node", "nodeid", relayNodeId.String())
|
||||
}
|
||||
} else {
|
||||
slog.Error("failed to relay node. maybe specified relay node is actually not a relay?", "err", err)
|
||||
}
|
||||
}
|
||||
logger.Log(1, "added new node", newNode.ID.String(), "to host", h.Name)
|
||||
|
|
27
auth/oidc.go
27
auth/oidc.go
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/gravitl/netmaker/database"
|
||||
"github.com/gravitl/netmaker/logger"
|
||||
"github.com/gravitl/netmaker/logic"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
|
@ -73,9 +74,29 @@ func handleOIDCCallback(w http.ResponseWriter, r *http.Request) {
|
|||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
if !isEmailAllowed(content.Email) {
|
||||
handleOauthUserNotAllowedToSignUp(w)
|
||||
return
|
||||
}
|
||||
// check if user approval is already pending
|
||||
if logic.IsPendingUser(content.Email) {
|
||||
handleOauthUserSignUpApprovalPending(w)
|
||||
return
|
||||
}
|
||||
_, err = logic.GetUser(content.Email)
|
||||
if err != nil { // user must not exists, so try to make one
|
||||
if err = addUser(content.Email); err != nil {
|
||||
if err != nil {
|
||||
if database.IsEmptyRecord(err) { // user must not exist, so try to make one
|
||||
err = logic.InsertPendingUser(&models.User{
|
||||
UserName: content.Email,
|
||||
})
|
||||
if err != nil {
|
||||
handleSomethingWentWrong(w)
|
||||
return
|
||||
}
|
||||
handleFirstTimeOauthUserSignUp(w)
|
||||
return
|
||||
} else {
|
||||
handleSomethingWentWrong(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +109,7 @@ func handleOIDCCallback(w http.ResponseWriter, r *http.Request) {
|
|||
handleOauthUserNotAllowed(w)
|
||||
return
|
||||
}
|
||||
var newPass, fetchErr = fetchPassValue("")
|
||||
var newPass, fetchErr = FetchPassValue("")
|
||||
if fetchErr != nil {
|
||||
return
|
||||
}
|
||||
|
|
20
cli/cmd/failover/disable.go
Normal file
20
cli/cmd/failover/disable.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package failover
|
||||
|
||||
import (
|
||||
"github.com/gravitl/netmaker/cli/functions"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var disableFailoverCmd = &cobra.Command{
|
||||
Use: "disable [NODE ID]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "Disable failover for a given Node",
|
||||
Long: `Disable failover for a given Node`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
functions.PrettyPrint(functions.DisableNodeFailover(args[0]))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(disableFailoverCmd)
|
||||
}
|
20
cli/cmd/failover/enable.go
Normal file
20
cli/cmd/failover/enable.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package failover
|
||||
|
||||
import (
|
||||
"github.com/gravitl/netmaker/cli/functions"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var enableFailoverCmd = &cobra.Command{
|
||||
Use: "enable [NODE ID]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "Enable failover for a given Node",
|
||||
Long: `Enable failover for a given Node`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
functions.PrettyPrint(functions.EnableNodeFailover(args[0]))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(enableFailoverCmd)
|
||||
}
|
28
cli/cmd/failover/root.go
Normal file
28
cli/cmd/failover/root.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package failover
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "failover",
|
||||
Short: "Enable/Disable failover for a node associated with a network",
|
||||
Long: `Enable/Disable failover for a node associated with a network`,
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ import (
|
|||
var (
|
||||
apiHostFilePath string
|
||||
endpoint string
|
||||
endpoint6 string
|
||||
name string
|
||||
listenPort int
|
||||
mtu int
|
||||
|
@ -40,6 +41,7 @@ var hostUpdateCmd = &cobra.Command{
|
|||
} else {
|
||||
apiHost.ID = args[0]
|
||||
apiHost.EndpointIP = endpoint
|
||||
apiHost.EndpointIPv6 = endpoint6
|
||||
apiHost.Name = name
|
||||
apiHost.ListenPort = listenPort
|
||||
apiHost.MTU = mtu
|
||||
|
@ -54,6 +56,7 @@ var hostUpdateCmd = &cobra.Command{
|
|||
func init() {
|
||||
hostUpdateCmd.Flags().StringVar(&apiHostFilePath, "file", "", "Path to host_definition.json")
|
||||
hostUpdateCmd.Flags().StringVar(&endpoint, "endpoint", "", "Endpoint of the Host")
|
||||
hostUpdateCmd.Flags().StringVar(&endpoint6, "endpoint6", "", "IPv6 Endpoint of the Host")
|
||||
hostUpdateCmd.Flags().StringVar(&name, "name", "", "Host name")
|
||||
hostUpdateCmd.Flags().IntVar(&listenPort, "listen_port", 0, "Listen port of the host")
|
||||
hostUpdateCmd.Flags().IntVar(&mtu, "mtu", 0, "Host MTU size")
|
||||
|
|
|
@ -32,6 +32,9 @@ var networkCreateCmd = &cobra.Command{
|
|||
network.AddressRange6 = address6
|
||||
network.IsIPv6 = "yes"
|
||||
}
|
||||
if address == "" {
|
||||
network.IsIPv4 = "no"
|
||||
}
|
||||
if udpHolePunch {
|
||||
network.DefaultUDPHolePunch = "yes"
|
||||
}
|
||||
|
|
|
@ -6,10 +6,11 @@ import (
|
|||
)
|
||||
|
||||
var nodeCreateIngressCmd = &cobra.Command{
|
||||
Use: "create_ingress [NETWORK NAME] [NODE ID]",
|
||||
Use: "create_remote_access_gateway [NETWORK NAME] [NODE ID]",
|
||||
Args: cobra.ExactArgs(2),
|
||||
Short: "Turn a Node into a Ingress",
|
||||
Long: `Turn a Node into a Ingress`,
|
||||
Short: "Turn a Node into a Remote Access Gateway (Ingress)",
|
||||
Long: `Turn a Node into a Remote Access Gateway (Ingress) for a Network.`,
|
||||
Aliases: []string{"create_rag"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
functions.PrettyPrint(functions.CreateIngress(args[0], args[1], failover))
|
||||
},
|
||||
|
|
|
@ -6,10 +6,11 @@ import (
|
|||
)
|
||||
|
||||
var nodeDeleteIngressCmd = &cobra.Command{
|
||||
Use: "delete_ingress [NETWORK NAME] [NODE ID]",
|
||||
Use: "delete_remote_access_gateway [NETWORK NAME] [NODE ID]",
|
||||
Args: cobra.ExactArgs(2),
|
||||
Short: "Delete Ingress role from a Node",
|
||||
Long: `Delete Ingress role from a Node`,
|
||||
Short: "Delete Remote Access Gateway role from a Node",
|
||||
Long: `Delete Remote Access Gateway role from a Node`,
|
||||
Aliases: []string{"delete_rag"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
functions.PrettyPrint(functions.DeleteIngress(args[0], args[1]))
|
||||
},
|
||||
|
|
|
@ -29,7 +29,7 @@ var nodeListCmd = &cobra.Command{
|
|||
functions.PrettyPrint(data)
|
||||
default:
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"ID", "Addresses", "Network", "Egress", "Ingress", "Relay"})
|
||||
table.SetHeader([]string{"ID", "Addresses", "Network", "Egress", "Remote Access Gateway", "Relay"})
|
||||
for _, d := range data {
|
||||
addresses := ""
|
||||
if d.Address != "" {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/gravitl/netmaker/cli/cmd/dns"
|
||||
"github.com/gravitl/netmaker/cli/cmd/enrollment_key"
|
||||
"github.com/gravitl/netmaker/cli/cmd/ext_client"
|
||||
"github.com/gravitl/netmaker/cli/cmd/failover"
|
||||
"github.com/gravitl/netmaker/cli/cmd/host"
|
||||
"github.com/gravitl/netmaker/cli/cmd/metrics"
|
||||
"github.com/gravitl/netmaker/cli/cmd/network"
|
||||
|
@ -53,4 +54,5 @@ func init() {
|
|||
rootCmd.AddCommand(metrics.GetRoot())
|
||||
rootCmd.AddCommand(host.GetRoot())
|
||||
rootCmd.AddCommand(enrollment_key.GetRoot())
|
||||
rootCmd.AddCommand(failover.GetRoot())
|
||||
}
|
||||
|
|
|
@ -14,5 +14,5 @@ func GetACL(networkName string) *acls.ACLContainer {
|
|||
|
||||
// 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)
|
||||
return request[acls.ACLContainer](http.MethodPut, fmt.Sprintf("/api/networks/%s/acls/v2", networkName), payload)
|
||||
}
|
||||
|
|
18
cli/functions/failover.go
Normal file
18
cli/functions/failover.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package functions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gravitl/netmaker/models"
|
||||
)
|
||||
|
||||
// EnableNodeFailover - Enable failover for a given Node
|
||||
func EnableNodeFailover(nodeID string) *models.SuccessResponse {
|
||||
return request[models.SuccessResponse](http.MethodPost, fmt.Sprintf("/api/v1/node/%s/failover", nodeID), nil)
|
||||
}
|
||||
|
||||
// DisableNodeFailover - Disable failover for a given Node
|
||||
func DisableNodeFailover(nodeID string) *models.SuccessResponse {
|
||||
return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/v1/node/%s/failover", nodeID), nil)
|
||||
}
|
|
@ -3,7 +3,7 @@ version: "3.4"
|
|||
services:
|
||||
netclient:
|
||||
container_name: netclient
|
||||
image: 'gravitl/netclient:v0.23.0'
|
||||
image: 'gravitl/netclient:v0.24.0'
|
||||
hostname: netmaker-1
|
||||
network_mode: host
|
||||
restart: on-failure
|
||||
|
|
|
@ -92,6 +92,8 @@ type ServerConfig struct {
|
|||
JwtValidityDuration time.Duration `yaml:"jwt_validity_duration"`
|
||||
RacAutoDisable bool `yaml:"rac_auto_disable"`
|
||||
CacheEnabled string `yaml:"caching_enabled"`
|
||||
EndpointDetection bool `json:"endpoint_detection"`
|
||||
AllowedEmailDomains string `yaml:"allowed_email_domains"`
|
||||
}
|
||||
|
||||
// SQLConfig - Generic SQL Config
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
//
|
||||
// Schemes: https
|
||||
// BasePath: /
|
||||
// Version: 0.23.0
|
||||
// Version: 0.24.0
|
||||
// Host: api.demo.netmaker.io
|
||||
//
|
||||
// Consumes:
|
||||
|
@ -49,6 +49,12 @@ type hasAdmin struct {
|
|||
Admin bool
|
||||
}
|
||||
|
||||
// swagger:response apiHostSliceResponse
|
||||
type apiHostSliceResponse struct {
|
||||
// in: body
|
||||
Host []models.ApiHost
|
||||
}
|
||||
|
||||
// swagger:response apiHostResponse
|
||||
type apiHostResponse struct {
|
||||
// in: body
|
||||
|
@ -251,7 +257,7 @@ type networkBodyResponse struct {
|
|||
Network models.Network `json:"network"`
|
||||
}
|
||||
|
||||
// swagger:parameters updateNetworkACL getNetworkACL
|
||||
// swagger:parameters updateNetworkACL
|
||||
type aclContainerBodyParam struct {
|
||||
// ACL Container
|
||||
// in: body
|
||||
|
@ -269,7 +275,7 @@ type aclContainerResponse struct {
|
|||
type nodeSliceResponse struct {
|
||||
// Nodes
|
||||
// in: body
|
||||
Nodes []models.LegacyNode `json:"nodes"`
|
||||
Nodes []models.ApiNode `json:"nodes"`
|
||||
}
|
||||
|
||||
// swagger:response nodeResponse
|
||||
|
@ -348,8 +354,26 @@ type HostFromNetworkParams struct {
|
|||
Network string `json:"network"`
|
||||
}
|
||||
|
||||
// swagger:parameters createEnrollmentKey
|
||||
type createEnrollmentKeyParams struct {
|
||||
// APIEnrollmentKey
|
||||
// in: body
|
||||
Body models.APIEnrollmentKey `json:"body"`
|
||||
}
|
||||
|
||||
// swagger:parameters updateEnrollmentKey
|
||||
type updateEnrollmentKeyParams struct {
|
||||
// KeyID
|
||||
// in: path
|
||||
KeyID string `json:"keyid"`
|
||||
|
||||
// APIEnrollmentKey
|
||||
// in: body
|
||||
Body models.APIEnrollmentKey `json:"body"`
|
||||
}
|
||||
|
||||
// swagger:parameters deleteEnrollmentKey
|
||||
type DeleteEnrollmentKeyParam struct {
|
||||
type deleteEnrollmentKeyParam struct {
|
||||
// in: path
|
||||
KeyID string `json:"keyid"`
|
||||
}
|
||||
|
@ -435,6 +459,7 @@ func useUnused() bool {
|
|||
_ = userAuthBodyParam{}
|
||||
_ = usernamePathParam{}
|
||||
_ = hasAdmin{}
|
||||
_ = apiHostSliceResponse{}
|
||||
_ = apiHostResponse{}
|
||||
_ = fileResponse{}
|
||||
_ = extClientConfParams{}
|
||||
|
@ -443,5 +468,8 @@ func useUnused() bool {
|
|||
_ = signal{}
|
||||
_ = filenameToGet{}
|
||||
_ = dnsNetworkPathParam{}
|
||||
_ = createEnrollmentKeyParams{}
|
||||
_ = updateEnrollmentKeyParams{}
|
||||
_ = deleteEnrollmentKeyParam{}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
|
|||
json.NewEncoder(w).Encode(newEnrollmentKey)
|
||||
}
|
||||
|
||||
// swagger:route PUT /api/v1/enrollment-keys/:id enrollmentKeys updateEnrollmentKey
|
||||
// swagger:route PUT /api/v1/enrollment-keys/{keyid} enrollmentKeys updateEnrollmentKey
|
||||
//
|
||||
// Updates an EnrollmentKey for hosts to use on Netmaker server. Updates only the relay to use.
|
||||
//
|
||||
|
@ -308,7 +308,7 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
|
|||
if !hostExists {
|
||||
newHost.PersistentKeepalive = models.DefaultPersistentKeepAlive
|
||||
// register host
|
||||
logic.CheckHostPorts(&newHost)
|
||||
//logic.CheckHostPorts(&newHost)
|
||||
// create EMQX credentials and ACLs for host
|
||||
if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
|
||||
if err := mq.GetEmqxHandler().CreateEmqxUser(newHost.ID.String(), newHost.HostPass); err != nil {
|
||||
|
|
|
@ -394,9 +394,9 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
for _, extclient := range extclients {
|
||||
if extclient.RemoteAccessClientID != "" &&
|
||||
extclient.RemoteAccessClientID == customExtClient.RemoteAccessClientID && nodeid == extclient.IngressGatewayID {
|
||||
extclient.RemoteAccessClientID == customExtClient.RemoteAccessClientID && extclient.OwnerID == caller.UserName && nodeid == extclient.IngressGatewayID {
|
||||
// extclient on the gw already exists for the remote access client
|
||||
err = errors.New("remote client config already exists on the gateway. it may have been created by another user with this same remote client machine")
|
||||
err = errors.New("remote client config already exists on the gateway")
|
||||
slog.Error("failed to create extclient", "user", userName, "error", err)
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
|
@ -436,15 +436,14 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
slog.Info("created extclient", "user", r.Header.Get("user"), "network", node.Network, "clientid", extclient.ClientID)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
go func() {
|
||||
if err := logic.SetClientDefaultACLs(&extclient); err != nil {
|
||||
slog.Error("failed to set default acls for extclient", "user", r.Header.Get("user"), "network", node.Network, "error", err)
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
return
|
||||
}
|
||||
|
||||
slog.Info("created extclient", "user", r.Header.Get("user"), "network", node.Network, "clientid", extclient.ClientID)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
go func() {
|
||||
if err := mq.PublishPeerUpdate(false); err != nil {
|
||||
logger.Log(1, "error setting ext peers on "+nodeid+": "+err.Error())
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ func hostHandlers(r *mux.Router) {
|
|||
r.HandleFunc("/api/v1/host", Authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet)
|
||||
r.HandleFunc("/api/v1/host/{hostid}/signalpeer", Authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/v1/fallback/host/{hostid}", Authorize(true, false, "host", http.HandlerFunc(hostUpdateFallback))).Methods(http.MethodPut)
|
||||
r.HandleFunc("/api/emqx/hosts", logic.SecurityCheck(true, http.HandlerFunc(delEmqxHosts))).Methods(http.MethodDelete)
|
||||
r.HandleFunc("/api/v1/auth-register/host", socketHandler)
|
||||
}
|
||||
|
||||
|
@ -61,7 +62,7 @@ func upgradeHost(w http.ResponseWriter, r *http.Request) {
|
|||
// oauth
|
||||
//
|
||||
// Responses:
|
||||
// 200: apiHostResponse
|
||||
// 200: apiHostSliceResponse
|
||||
func getHosts(w http.ResponseWriter, r *http.Request) {
|
||||
currentHosts, err := logic.GetAllHosts()
|
||||
if err != nil {
|
||||
|
@ -144,6 +145,7 @@ func pull(w http.ResponseWriter, r *http.Request) {
|
|||
ChangeDefaultGw: hPU.ChangeDefaultGw,
|
||||
DefaultGwIp: hPU.DefaultGwIp,
|
||||
IsInternetGw: hPU.IsInternetGw,
|
||||
EndpointDetection: servercfg.IsEndpointDetectionEnabled(),
|
||||
}
|
||||
|
||||
logger.Log(1, hostID, "completed a pull")
|
||||
|
@ -552,7 +554,7 @@ func authenticateHost(response http.ResponseWriter, request *http.Request) {
|
|||
logic.ReturnErrorResponse(response, request, errorResponse)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
// Create EMQX creds and ACLs if not found
|
||||
if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
|
||||
if err := mq.GetEmqxHandler().CreateEmqxUser(host.ID.String(), authRequest.Password); err != nil {
|
||||
|
@ -572,6 +574,7 @@ func authenticateHost(response http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
response.WriteHeader(http.StatusOK)
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
|
@ -749,3 +752,34 @@ func syncHost(w http.ResponseWriter, r *http.Request) {
|
|||
slog.Info("requested host pull", "user", r.Header.Get("user"), "host", host.ID)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
// swagger:route DELETE /api/emqx/hosts hosts delEmqxHosts
|
||||
//
|
||||
// Lists all hosts.
|
||||
//
|
||||
// Schemes: https
|
||||
//
|
||||
// Security:
|
||||
// oauth
|
||||
//
|
||||
// Responses:
|
||||
// 200: apiHostResponse
|
||||
func delEmqxHosts(w http.ResponseWriter, r *http.Request) {
|
||||
currentHosts, err := logic.GetAllHosts()
|
||||
if err != nil {
|
||||
logger.Log(0, r.Header.Get("user"), "failed to fetch hosts: ", err.Error())
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
return
|
||||
}
|
||||
for _, host := range currentHosts {
|
||||
// delete EMQX credentials for host
|
||||
if err := mq.GetEmqxHandler().DeleteEmqxUser(host.ID.String()); err != nil {
|
||||
slog.Error("failed to remove host credentials from EMQX", "id", host.ID, "error", err)
|
||||
}
|
||||
}
|
||||
err = mq.GetEmqxHandler().DeleteEmqxUser(servercfg.GetMqUserName())
|
||||
if err != nil {
|
||||
slog.Error("failed to remove server credentials from EMQX", "user", servercfg.GetMqUserName(), "error", err)
|
||||
}
|
||||
logic.ReturnSuccessResponse(w, r, "deleted hosts data on emqx")
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ func getUsage(w http.ResponseWriter, _ *http.Request) {
|
|||
Egresses int `json:"egresses"`
|
||||
Relays int `json:"relays"`
|
||||
InternetGateways int `json:"internet_gateways"`
|
||||
FailOvers int `json:"fail_overs"`
|
||||
}
|
||||
var serverUsage usage
|
||||
hosts, err := logic.GetAllHosts()
|
||||
|
@ -90,6 +91,10 @@ func getUsage(w http.ResponseWriter, _ *http.Request) {
|
|||
if err == nil {
|
||||
serverUsage.InternetGateways = len(gateways)
|
||||
}
|
||||
failOvers, err := logic.GetAllFailOvers()
|
||||
if err == nil {
|
||||
serverUsage.FailOvers = len(failOvers)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(models.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/gravitl/netmaker/auth"
|
||||
"github.com/gravitl/netmaker/database"
|
||||
"github.com/gravitl/netmaker/logger"
|
||||
"github.com/gravitl/netmaker/logic"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
|
@ -35,6 +36,11 @@ func userHandlers(r *mux.Router) {
|
|||
r.HandleFunc("/api/oauth/callback", auth.HandleAuthCallback).Methods(http.MethodGet)
|
||||
r.HandleFunc("/api/oauth/headless", auth.HandleHeadlessSSO)
|
||||
r.HandleFunc("/api/oauth/register/{regKey}", auth.RegisterHostSSO).Methods(http.MethodGet)
|
||||
r.HandleFunc("/api/users_pending", logic.SecurityCheck(true, http.HandlerFunc(getPendingUsers))).Methods(http.MethodGet)
|
||||
r.HandleFunc("/api/users_pending", logic.SecurityCheck(true, http.HandlerFunc(deleteAllPendingUsers))).Methods(http.MethodDelete)
|
||||
r.HandleFunc("/api/users_pending/user/{username}", logic.SecurityCheck(true, http.HandlerFunc(deletePendingUser))).Methods(http.MethodDelete)
|
||||
r.HandleFunc("/api/users_pending/user/{username}", logic.SecurityCheck(true, http.HandlerFunc(approvePendingUser))).Methods(http.MethodPost)
|
||||
|
||||
}
|
||||
|
||||
// swagger:route POST /api/users/adm/authenticate authenticate authenticateUser
|
||||
|
@ -583,3 +589,136 @@ func socketHandler(w http.ResponseWriter, r *http.Request) {
|
|||
// Start handling the session
|
||||
go auth.SessionHandler(conn)
|
||||
}
|
||||
|
||||
// swagger:route GET /api/users_pending user getPendingUsers
|
||||
//
|
||||
// Get all pending users.
|
||||
//
|
||||
// Schemes: https
|
||||
//
|
||||
// Security:
|
||||
// oauth
|
||||
//
|
||||
// Responses:
|
||||
// 200: userBodyResponse
|
||||
func getPendingUsers(w http.ResponseWriter, r *http.Request) {
|
||||
// set header.
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
users, err := logic.ListPendingUsers()
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to fetch users: ", err.Error())
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
return
|
||||
}
|
||||
|
||||
logic.SortUsers(users[:])
|
||||
logger.Log(2, r.Header.Get("user"), "fetched pending users")
|
||||
json.NewEncoder(w).Encode(users)
|
||||
}
|
||||
|
||||
// swagger:route POST /api/users_pending/user/{username} user approvePendingUser
|
||||
//
|
||||
// approve pending user.
|
||||
//
|
||||
// Schemes: https
|
||||
//
|
||||
// Security:
|
||||
// oauth
|
||||
//
|
||||
// Responses:
|
||||
// 200: userBodyResponse
|
||||
func approvePendingUser(w http.ResponseWriter, r *http.Request) {
|
||||
// set header.
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
var params = mux.Vars(r)
|
||||
username := params["username"]
|
||||
users, err := logic.ListPendingUsers()
|
||||
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to fetch users: ", err.Error())
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
return
|
||||
}
|
||||
for _, user := range users {
|
||||
if user.UserName == username {
|
||||
var newPass, fetchErr = auth.FetchPassValue("")
|
||||
if fetchErr != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(fetchErr, "internal"))
|
||||
return
|
||||
}
|
||||
if err = logic.CreateUser(&models.User{
|
||||
UserName: user.UserName,
|
||||
Password: newPass,
|
||||
}); err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to create user: %s", err), "internal"))
|
||||
return
|
||||
}
|
||||
err = logic.DeletePendingUser(username)
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete pending user: %s", err), "internal"))
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
logic.ReturnSuccessResponse(w, r, "approved "+username)
|
||||
}
|
||||
|
||||
// swagger:route DELETE /api/users_pending/user/{username} user deletePendingUser
|
||||
//
|
||||
// delete pending user.
|
||||
//
|
||||
// Schemes: https
|
||||
//
|
||||
// Security:
|
||||
// oauth
|
||||
//
|
||||
// Responses:
|
||||
// 200: userBodyResponse
|
||||
func deletePendingUser(w http.ResponseWriter, r *http.Request) {
|
||||
// set header.
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
var params = mux.Vars(r)
|
||||
username := params["username"]
|
||||
users, err := logic.ListPendingUsers()
|
||||
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to fetch users: ", err.Error())
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
return
|
||||
}
|
||||
for _, user := range users {
|
||||
if user.UserName == username {
|
||||
err = logic.DeletePendingUser(username)
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete pending user: %s", err), "internal"))
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
logic.ReturnSuccessResponse(w, r, "deleted pending "+username)
|
||||
}
|
||||
|
||||
// swagger:route DELETE /api/users_pending/{username}/pending user deleteAllPendingUsers
|
||||
//
|
||||
// delete all pending users.
|
||||
//
|
||||
// Schemes: https
|
||||
//
|
||||
// Security:
|
||||
// oauth
|
||||
//
|
||||
// Responses:
|
||||
// 200: userBodyResponse
|
||||
func deleteAllPendingUsers(w http.ResponseWriter, r *http.Request) {
|
||||
// set header.
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
err := database.DeleteAllRecords(database.PENDING_USERS_TABLE_NAME)
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to delete all pending users "+err.Error()), "internal"))
|
||||
return
|
||||
}
|
||||
logic.ReturnSuccessResponse(w, r, "cleared all pending users")
|
||||
}
|
||||
|
|
|
@ -61,7 +61,8 @@ const (
|
|||
ENROLLMENT_KEYS_TABLE_NAME = "enrollmentkeys"
|
||||
// HOST_ACTIONS_TABLE_NAME - table name for enrollmentkeys
|
||||
HOST_ACTIONS_TABLE_NAME = "hostactions"
|
||||
|
||||
// PENDING_USERS_TABLE_NAME - table name for pending users
|
||||
PENDING_USERS_TABLE_NAME = "pending_users"
|
||||
// == ERROR CONSTS ==
|
||||
// NO_RECORD - no singular result found
|
||||
NO_RECORD = "no result found"
|
||||
|
@ -144,6 +145,7 @@ func createTables() {
|
|||
CreateTable(HOSTS_TABLE_NAME)
|
||||
CreateTable(ENROLLMENT_KEYS_TABLE_NAME)
|
||||
CreateTable(HOST_ACTIONS_TABLE_NAME)
|
||||
CreateTable(PENDING_USERS_TABLE_NAME)
|
||||
}
|
||||
|
||||
func CreateTable(tableName string) error {
|
||||
|
|
14
go.mod
14
go.mod
|
@ -4,7 +4,7 @@ go 1.19
|
|||
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.3
|
||||
github.com/go-playground/validator/v10 v10.18.0
|
||||
github.com/go-playground/validator/v10 v10.19.0
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/handlers v1.5.2
|
||||
|
@ -13,12 +13,12 @@ require (
|
|||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/rqlite/gorqlite v0.0.0-20240122221808-a8a425b1a6aa
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/txn2/txeh v1.5.5
|
||||
golang.org/x/crypto v0.19.0
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/oauth2 v0.17.0
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/crypto v0.22.0
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/oauth2 v0.18.0
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
|
@ -38,7 +38,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/go-jose/go-jose/v3 v3.0.1
|
||||
github.com/go-jose/go-jose/v3 v3.0.3
|
||||
github.com/guumaster/tablewriter v0.0.10
|
||||
github.com/matryer/is v1.4.1
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
|
|
44
go.sum
44
go.sum
|
@ -20,24 +20,24 @@ github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBd
|
|||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
|
||||
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U=
|
||||
github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
|
||||
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
|
||||
|
@ -85,9 +85,9 @@ github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyh
|
|||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/txn2/txeh v1.5.5 h1:UN4e/lCK5HGw/gGAi2GCVrNKg0GTCUWs7gs5riaZlz4=
|
||||
github.com/txn2/txeh v1.5.5/go.mod h1:qYzGG9kCzeVEI12geK4IlanHWY8X4uy/I3NcW7mk8g4=
|
||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
|
@ -95,44 +95,54 @@ github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEAB
|
|||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
|
||||
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb h1:9aqVcYEDHmSNb0uOWukxV5lHV09WqiSiCuhEgWNETLY=
|
||||
|
|
|
@ -16,7 +16,7 @@ spec:
|
|||
hostNetwork: true
|
||||
containers:
|
||||
- name: netclient
|
||||
image: gravitl/netclient:v0.23.0
|
||||
image: gravitl/netclient:v0.24.0
|
||||
env:
|
||||
- name: TOKEN
|
||||
value: "TOKEN_VALUE"
|
||||
|
|
|
@ -28,7 +28,7 @@ spec:
|
|||
# - "<node label value>"
|
||||
containers:
|
||||
- name: netclient
|
||||
image: gravitl/netclient:v0.23.0
|
||||
image: gravitl/netclient:v0.24.0
|
||||
env:
|
||||
- name: TOKEN
|
||||
value: "TOKEN_VALUE"
|
||||
|
|
|
@ -15,7 +15,7 @@ spec:
|
|||
spec:
|
||||
containers:
|
||||
- name: netmaker-ui
|
||||
image: gravitl/netmaker-ui:v0.23.0
|
||||
image: gravitl/netmaker-ui:v0.24.0
|
||||
ports:
|
||||
- containerPort: 443
|
||||
env:
|
||||
|
|
|
@ -64,9 +64,9 @@ func (acl ACL) Save(containerID ContainerID, ID AclID) (ACL, error) {
|
|||
|
||||
// ACL.IsAllowed - sees if ID is allowed in referring ACL
|
||||
func (acl ACL) IsAllowed(ID AclID) (allowed bool) {
|
||||
AclMutex.RLock()
|
||||
AclMutex.Lock()
|
||||
allowed = acl[ID] == Allowed
|
||||
AclMutex.RUnlock()
|
||||
AclMutex.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,8 @@ func (aclContainer ACLContainer) RemoveACL(ID AclID) ACLContainer {
|
|||
|
||||
// ACLContainer.ChangeAccess - changes the relationship between two nodes in memory
|
||||
func (networkACL ACLContainer) ChangeAccess(ID1, ID2 AclID, value byte) {
|
||||
AclMutex.Lock()
|
||||
defer AclMutex.Unlock()
|
||||
if _, ok := networkACL[ID1]; !ok {
|
||||
slog.Error("ACL missing for ", "id", ID1)
|
||||
return
|
||||
|
|
|
@ -3,21 +3,26 @@ package nodeacls
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/gravitl/netmaker/logic/acls"
|
||||
)
|
||||
|
||||
var NodesAllowedACLMutex = &sync.Mutex{}
|
||||
|
||||
// AreNodesAllowed - checks if nodes are allowed to communicate in their network ACL
|
||||
func AreNodesAllowed(networkID NetworkID, node1, node2 NodeID) bool {
|
||||
NodesAllowedACLMutex.Lock()
|
||||
defer NodesAllowedACLMutex.Unlock()
|
||||
var currentNetworkACL, err = FetchAllACLs(networkID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var allowed bool
|
||||
acls.AclMutex.RLock()
|
||||
acls.AclMutex.Lock()
|
||||
currNetworkACLNode1 := currentNetworkACL[acls.AclID(node1)]
|
||||
currNetworkACLNode2 := currentNetworkACL[acls.AclID(node2)]
|
||||
acls.AclMutex.RUnlock()
|
||||
acls.AclMutex.Unlock()
|
||||
allowed = currNetworkACLNode1.IsAllowed(acls.AclID(node2)) && currNetworkACLNode2.IsAllowed(acls.AclID(node1))
|
||||
return allowed
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -15,6 +16,10 @@ import (
|
|||
"github.com/gravitl/netmaker/models"
|
||||
)
|
||||
|
||||
const (
|
||||
auth_key = "netmaker_auth"
|
||||
)
|
||||
|
||||
// HasSuperAdmin - checks if server has an superadmin/owner
|
||||
func HasSuperAdmin() (bool, error) {
|
||||
|
||||
|
@ -96,12 +101,14 @@ func CreateUser(user *models.User) error {
|
|||
}
|
||||
var err = ValidateUser(user)
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to validate user", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// encrypt that password so we never see it again
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 5)
|
||||
if err != nil {
|
||||
logger.Log(0, "error encrypting pass", err.Error())
|
||||
return err
|
||||
}
|
||||
// set password to encrypted password
|
||||
|
@ -109,6 +116,7 @@ func CreateUser(user *models.User) error {
|
|||
|
||||
tokenString, _ := CreateUserJWT(user.UserName, user.IsSuperAdmin, user.IsAdmin)
|
||||
if tokenString == "" {
|
||||
logger.Log(0, "failed to generate token", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -117,10 +125,12 @@ func CreateUser(user *models.User) error {
|
|||
// connect db
|
||||
data, err := json.Marshal(user)
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to marshal", err.Error())
|
||||
return err
|
||||
}
|
||||
err = database.Insert(user.UserName, string(data), database.USERS_TABLE_NAME)
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to insert user", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -279,15 +289,31 @@ func DeleteUser(user string) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// FetchAuthSecret - manages secrets for oauth
|
||||
func FetchAuthSecret(key string, secret string) (string, error) {
|
||||
var record, err = database.FetchRecord(database.GENERATED_TABLE_NAME, key)
|
||||
if err != nil {
|
||||
if err = database.Insert(key, secret, database.GENERATED_TABLE_NAME); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return secret, nil
|
||||
func SetAuthSecret(secret string) error {
|
||||
type valueHolder struct {
|
||||
Value string `json:"value" bson:"value"`
|
||||
}
|
||||
record, err := FetchAuthSecret()
|
||||
if err == nil {
|
||||
v := valueHolder{}
|
||||
json.Unmarshal([]byte(record), &v)
|
||||
if v.Value != "" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var b64NewValue = base64.StdEncoding.EncodeToString([]byte(secret))
|
||||
newValueHolder := valueHolder{
|
||||
Value: b64NewValue,
|
||||
}
|
||||
d, _ := json.Marshal(newValueHolder)
|
||||
return database.Insert(auth_key, string(d), database.GENERATED_TABLE_NAME)
|
||||
}
|
||||
|
||||
// FetchAuthSecret - manages secrets for oauth
|
||||
func FetchAuthSecret() (string, error) {
|
||||
var record, err = database.FetchRecord(database.GENERATED_TABLE_NAME, auth_key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return record, nil
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gravitl/netmaker/logger"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// FormatError - takes ErrorResponse and uses correct code
|
||||
|
@ -62,7 +62,7 @@ func ReturnErrorResponse(response http.ResponseWriter, request *http.Request, er
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logger.Log(1, "processed request error:", errorMessage.Message)
|
||||
slog.Debug("processed request error", "err", errorMessage.Message)
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
response.WriteHeader(errorMessage.Code)
|
||||
response.Write(jsonResponse)
|
||||
|
|
|
@ -164,6 +164,11 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq
|
|||
node.IngressGatewayRange = network.AddressRange
|
||||
node.IngressGatewayRange6 = network.AddressRange6
|
||||
node.IngressDNS = ingress.ExtclientDNS
|
||||
if servercfg.IsPro {
|
||||
if _, exists := FailOverExists(node.Network); exists {
|
||||
ResetFailedOverPeer(&node)
|
||||
}
|
||||
}
|
||||
node.SetLastModified()
|
||||
if node.Metadata == "" {
|
||||
node.Metadata = "This host can be used for remote access"
|
||||
|
|
|
@ -217,6 +217,7 @@ func UpdateHost(newHost, currentHost *models.Host) {
|
|||
newHost.Nodes = currentHost.Nodes
|
||||
newHost.PublicKey = currentHost.PublicKey
|
||||
newHost.TrafficKeyPublic = currentHost.TrafficKeyPublic
|
||||
newHost.EndpointIPv6 = currentHost.EndpointIPv6
|
||||
// changeable fields
|
||||
if len(newHost.Version) == 0 {
|
||||
newHost.Version = currentHost.Version
|
||||
|
@ -258,6 +259,10 @@ func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool)
|
|||
currHost.EndpointIP = newHost.EndpointIP
|
||||
sendPeerUpdate = true
|
||||
}
|
||||
if currHost.EndpointIPv6.String() != newHost.EndpointIPv6.String() {
|
||||
currHost.EndpointIPv6 = newHost.EndpointIPv6
|
||||
sendPeerUpdate = true
|
||||
}
|
||||
currHost.DaemonInstalled = newHost.DaemonInstalled
|
||||
currHost.Debug = newHost.Debug
|
||||
currHost.Verbosity = newHost.Verbosity
|
||||
|
|
|
@ -106,7 +106,6 @@ func VerifyUserToken(tokenString string) (username string, issuperadmin, isadmin
|
|||
if err != nil {
|
||||
return "", false, false, err
|
||||
}
|
||||
|
||||
if user.UserName != "" {
|
||||
return user.UserName, user.IsSuperAdmin, user.IsAdmin, nil
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ func GetParentNetwork(networkname string) (models.Network, error) {
|
|||
return network, nil
|
||||
}
|
||||
|
||||
// GetParentNetwork - get parent network
|
||||
// GetNetworkSettings - get parent network
|
||||
func GetNetworkSettings(networkname string) (models.Network, error) {
|
||||
|
||||
var network models.Network
|
||||
|
|
|
@ -625,3 +625,18 @@ func ValidateParams(nodeid, netid string) (models.Node, error) {
|
|||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// GetAllFailOvers - gets all the nodes that are failovers
|
||||
func GetAllFailOvers() ([]models.Node, error) {
|
||||
nodes, err := GetAllNodes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
igs := make([]models.Node, 0)
|
||||
for _, node := range nodes {
|
||||
if node.IsFailOver {
|
||||
igs = append(igs, node)
|
||||
}
|
||||
}
|
||||
return igs, nil
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ var (
|
|||
ResetFailedOverPeer = func(failedOverNode *models.Node) error {
|
||||
return nil
|
||||
}
|
||||
// FailOverExists - check if failover node existed or not
|
||||
FailOverExists = func(network string) (failOverNode models.Node, exists bool) {
|
||||
return failOverNode, exists
|
||||
}
|
||||
// GetFailOverPeerIps - gets failover peerips
|
||||
GetFailOverPeerIps = func(peer, node *models.Node) []net.IPNet {
|
||||
return []net.IPNet{}
|
||||
|
@ -76,6 +80,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
|
|||
Peers: []wgtypes.PeerConfig{},
|
||||
NodePeers: []wgtypes.PeerConfig{},
|
||||
HostNetworkInfo: models.HostInfoMap{},
|
||||
EndpointDetection: servercfg.IsEndpointDetectionEnabled(),
|
||||
}
|
||||
|
||||
slog.Debug("peer update for host", "hostId", host.ID.String())
|
||||
|
@ -168,7 +173,8 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
|
|||
}
|
||||
if peer.IsEgressGateway {
|
||||
hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, models.EgressNetworkRoutes{
|
||||
NodeAddr: node.PrimaryAddressIPNet(),
|
||||
NodeAddr: node.Address,
|
||||
NodeAddr6: node.Address6,
|
||||
EgressRanges: peer.EgressGatewayRanges,
|
||||
})
|
||||
}
|
||||
|
@ -206,8 +212,21 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
|
|||
uselocal = false
|
||||
}
|
||||
}
|
||||
|
||||
//if host is ipv4 only or ipv4+ipv6, set the peer endpoint to ipv4 address, if host is ipv6 only, set the peer endpoint to ipv6 address
|
||||
peerEndpoint := peerHost.EndpointIP
|
||||
if ipv4 := host.EndpointIP.To4(); ipv4 != nil {
|
||||
peerEndpoint = peerHost.EndpointIP
|
||||
} else {
|
||||
//if peer host's ipv6 address is empty, it means that peer is an IPv4 only host
|
||||
//IPv4 only host could not communicate with IPv6 only host
|
||||
if peerHost.EndpointIPv6 != nil && peerHost.EndpointIPv6.String() != "" {
|
||||
peerEndpoint = peerHost.EndpointIPv6
|
||||
}
|
||||
}
|
||||
|
||||
peerConfig.Endpoint = &net.UDPAddr{
|
||||
IP: peerHost.EndpointIP,
|
||||
IP: peerEndpoint,
|
||||
Port: GetPeerListenPort(peerHost),
|
||||
}
|
||||
|
||||
|
@ -371,6 +390,7 @@ func GetPeerListenPort(host *models.Host) int {
|
|||
// GetAllowedIPs - calculates the wireguard allowedip field for a peer of a node based on the peer and node settings
|
||||
func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet {
|
||||
var allowedips []net.IPNet
|
||||
allowedips = getNodeAllowedIPs(peer, node)
|
||||
if peer.IsInternetGateway && node.InternetGwID == peer.ID.String() {
|
||||
allowedips = append(allowedips, GetAllowedIpForInetNodeClient(node, peer)...)
|
||||
return allowedips
|
||||
|
@ -381,7 +401,6 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
|
|||
return allowedips
|
||||
}
|
||||
}
|
||||
allowedips = append(allowedips, getNodeAllowedIPs(peer, node)...)
|
||||
|
||||
// handle ingress gateway peers
|
||||
if peer.IsIngressGateway {
|
||||
|
|
|
@ -39,10 +39,7 @@ func sendTelemetry() error {
|
|||
return err
|
||||
}
|
||||
// get telemetry data
|
||||
d, err := FetchTelemetryData()
|
||||
if err != nil {
|
||||
slog.Error("error fetching telemetry data", "error", err)
|
||||
}
|
||||
d := FetchTelemetryData()
|
||||
// get tenant admin email
|
||||
adminEmail := os.Getenv("NM_EMAIL")
|
||||
client, err := posthog.NewWithConfig(posthog_pub_key, posthog.Config{Endpoint: posthog_endpoint})
|
||||
|
@ -82,7 +79,7 @@ func sendTelemetry() error {
|
|||
}
|
||||
|
||||
// FetchTelemetryData - fetches telemetry data: count of various object types in DB
|
||||
func FetchTelemetryData() (telemetryData, error) {
|
||||
func FetchTelemetryData() telemetryData {
|
||||
var data telemetryData
|
||||
|
||||
data.IsPro = servercfg.IsPro
|
||||
|
@ -92,21 +89,16 @@ func FetchTelemetryData() (telemetryData, error) {
|
|||
data.Hosts = getDBLength(database.HOSTS_TABLE_NAME)
|
||||
data.Version = servercfg.GetVersion()
|
||||
data.Servers = getServerCount()
|
||||
nodes, err := GetAllNodes()
|
||||
if err == nil {
|
||||
nodes, _ := GetAllNodes()
|
||||
data.Nodes = len(nodes)
|
||||
data.Count = getClientCount(nodes)
|
||||
}
|
||||
endDate, err := GetTrialEndDate()
|
||||
if err != nil {
|
||||
logger.Log(0, "error getting trial end date", err.Error())
|
||||
}
|
||||
endDate, _ := GetTrialEndDate()
|
||||
data.ProTrialEndDate = endDate
|
||||
if endDate.After(time.Now()) {
|
||||
data.IsProTrial = true
|
||||
}
|
||||
data.IsSaasTenant = servercfg.DeployedByOperator()
|
||||
return data, err
|
||||
return data
|
||||
}
|
||||
|
||||
// getServerCount returns number of servers from database
|
||||
|
|
|
@ -75,3 +75,47 @@ func GetSuperAdmin() (models.ReturnUser, error) {
|
|||
}
|
||||
return models.ReturnUser{}, errors.New("superadmin not found")
|
||||
}
|
||||
|
||||
func InsertPendingUser(u *models.User) error {
|
||||
data, err := json.Marshal(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return database.Insert(u.UserName, string(data), database.PENDING_USERS_TABLE_NAME)
|
||||
}
|
||||
|
||||
func DeletePendingUser(username string) error {
|
||||
return database.DeleteRecord(database.PENDING_USERS_TABLE_NAME, username)
|
||||
}
|
||||
|
||||
func IsPendingUser(username string) bool {
|
||||
records, err := database.FetchRecords(database.PENDING_USERS_TABLE_NAME)
|
||||
if err != nil {
|
||||
return false
|
||||
|
||||
}
|
||||
for _, record := range records {
|
||||
u := models.ReturnUser{}
|
||||
err := json.Unmarshal([]byte(record), &u)
|
||||
if err == nil && u.UserName == username {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ListPendingUsers() ([]models.ReturnUser, error) {
|
||||
pendingUsers := []models.ReturnUser{}
|
||||
records, err := database.FetchRecords(database.PENDING_USERS_TABLE_NAME)
|
||||
if err != nil && !database.IsEmptyRecord(err) {
|
||||
return pendingUsers, err
|
||||
}
|
||||
for _, record := range records {
|
||||
u := models.ReturnUser{}
|
||||
err = json.Unmarshal([]byte(record), &u)
|
||||
if err == nil {
|
||||
pendingUsers = append(pendingUsers, u)
|
||||
}
|
||||
}
|
||||
return pendingUsers, nil
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ func StringSliceContains(slice []string, item string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// NormalCIDR - returns the first address of CIDR
|
||||
// NormalizeCIDR - returns the first address of CIDR
|
||||
func NormalizeCIDR(address string) (string, error) {
|
||||
ip, IPNet, err := net.ParseCIDR(address)
|
||||
if err != nil {
|
||||
|
|
|
@ -76,7 +76,7 @@ func checkForZombieHosts(h *models.Host) {
|
|||
// ManageZombies - goroutine which adds/removes/deletes nodes from the zombie node quarantine list
|
||||
func ManageZombies(ctx context.Context, peerUpdate chan *models.Node) {
|
||||
logger.Log(2, "Zombie management started")
|
||||
InitializeZombies()
|
||||
go InitializeZombies()
|
||||
|
||||
// Zombie Nodes Cleanup Four Times a Day
|
||||
ticker := time.NewTicker(time.Hour * ZOMBIE_TIMEOUT)
|
||||
|
|
4
main.go
4
main.go
|
@ -28,7 +28,7 @@ import (
|
|||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
var version = "v0.23.0"
|
||||
var version = "v0.24.0"
|
||||
|
||||
// Start DB Connection and start API Request Handler
|
||||
func main() {
|
||||
|
@ -155,7 +155,7 @@ func runMessageQueue(wg *sync.WaitGroup, ctx context.Context) {
|
|||
defer wg.Done()
|
||||
brokerHost, _ := servercfg.GetMessageQueueEndpoint()
|
||||
logger.Log(0, "connecting to mq broker at", brokerHost)
|
||||
mq.SetupMQTT()
|
||||
mq.SetupMQTT(true)
|
||||
if mq.IsConnected() {
|
||||
logger.Log(0, "connected to MQ Broker")
|
||||
} else {
|
||||
|
|
|
@ -177,7 +177,7 @@ func removeInterGw(egressRanges []string) ([]string, bool) {
|
|||
func updateAcls() {
|
||||
// get all networks
|
||||
networks, err := logic.GetNetworks()
|
||||
if err != nil {
|
||||
if err != nil && !database.IsEmptyRecord(err) {
|
||||
slog.Error("acls migration failed. error getting networks", "error", err)
|
||||
return
|
||||
}
|
||||
|
@ -287,7 +287,7 @@ func updateAcls() {
|
|||
}
|
||||
|
||||
// save new acls
|
||||
slog.Info(fmt.Sprintf("(migration) saving new acls for network: %s", network.NetID), "networkAcl", networkAcl)
|
||||
slog.Debug(fmt.Sprintf("(migration) saving new acls for network: %s", network.NetID), "networkAcl", networkAcl)
|
||||
if _, err := networkAcl.Save(acls.ContainerID(network.NetID)); err != nil {
|
||||
slog.Error(fmt.Sprintf("error during acls migration. error saving new acls for network: %s", network.NetID), "error", err)
|
||||
continue
|
||||
|
|
|
@ -19,9 +19,10 @@ type ApiHost struct {
|
|||
ListenPort int `json:"listenport"`
|
||||
WgPublicListenPort int `json:"wg_public_listen_port" yaml:"wg_public_listen_port"`
|
||||
MTU int `json:"mtu" yaml:"mtu"`
|
||||
Interfaces []Iface `json:"interfaces" yaml:"interfaces"`
|
||||
Interfaces []ApiIface `json:"interfaces" yaml:"interfaces"`
|
||||
DefaultInterface string `json:"defaultinterface" yaml:"defautlinterface"`
|
||||
EndpointIP string `json:"endpointip" yaml:"endpointip"`
|
||||
EndpointIPv6 string `json:"endpointipv6" yaml:"endpointipv6"`
|
||||
PublicKey string `json:"publickey"`
|
||||
MacAddress string `json:"macaddress"`
|
||||
Nodes []string `json:"nodes"`
|
||||
|
@ -31,16 +32,27 @@ type ApiHost struct {
|
|||
AutoUpdate bool `json:"autoupdate" yaml:"autoupdate"`
|
||||
}
|
||||
|
||||
// ApiIface - the interface struct for API usage
|
||||
// The original Iface struct contains a net.Address, which does not get marshalled correctly
|
||||
type ApiIface struct {
|
||||
Name string `json:"name"`
|
||||
AddressString string `json:"addressString"`
|
||||
}
|
||||
|
||||
// Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host
|
||||
func (h *Host) ConvertNMHostToAPI() *ApiHost {
|
||||
a := ApiHost{}
|
||||
a.Debug = h.Debug
|
||||
a.EndpointIP = h.EndpointIP.String()
|
||||
a.EndpointIPv6 = h.EndpointIPv6.String()
|
||||
a.FirewallInUse = h.FirewallInUse
|
||||
a.ID = h.ID.String()
|
||||
a.Interfaces = h.Interfaces
|
||||
a.Interfaces = make([]ApiIface, len(h.Interfaces))
|
||||
for i := range a.Interfaces {
|
||||
a.Interfaces[i].AddressString = a.Interfaces[i].Address.String()
|
||||
a.Interfaces[i] = ApiIface{
|
||||
Name: h.Interfaces[i].Name,
|
||||
AddressString: h.Interfaces[i].Address.String(),
|
||||
}
|
||||
}
|
||||
a.DefaultInterface = h.DefaultInterface
|
||||
a.IsStatic = h.IsStatic
|
||||
|
@ -73,6 +85,11 @@ func (a *ApiHost) ConvertAPIHostToNMHost(currentHost *Host) *Host {
|
|||
} else {
|
||||
h.EndpointIP = net.ParseIP(a.EndpointIP)
|
||||
}
|
||||
if len(a.EndpointIPv6) == 0 || strings.Contains(a.EndpointIPv6, "nil") {
|
||||
h.EndpointIPv6 = currentHost.EndpointIPv6
|
||||
} else {
|
||||
h.EndpointIPv6 = net.ParseIP(a.EndpointIPv6)
|
||||
}
|
||||
h.Debug = a.Debug
|
||||
h.FirewallInUse = a.FirewallInUse
|
||||
h.IPForwarding = currentHost.IPForwarding
|
||||
|
|
|
@ -63,6 +63,7 @@ type Host struct {
|
|||
Interfaces []Iface `json:"interfaces" yaml:"interfaces"`
|
||||
DefaultInterface string `json:"defaultinterface" yaml:"defaultinterface"`
|
||||
EndpointIP net.IP `json:"endpointip" yaml:"endpointip"`
|
||||
EndpointIPv6 net.IP `json:"endpointipv6" yaml:"endpointipv6"`
|
||||
IsDocker bool `json:"isdocker" yaml:"isdocker"`
|
||||
IsK8S bool `json:"isk8s" yaml:"isk8s"`
|
||||
IsStatic bool `json:"isstatic" yaml:"isstatic"`
|
||||
|
|
|
@ -23,6 +23,7 @@ type HostPeerUpdate struct {
|
|||
EgressRoutes []EgressNetworkRoutes `json:"egress_network_routes"`
|
||||
FwUpdate FwUpdate `json:"fw_update"`
|
||||
ReplacePeers bool `json:"replace_peers"`
|
||||
EndpointDetection bool `json:"endpoint_detection"`
|
||||
}
|
||||
|
||||
// IngressInfo - struct for ingress info
|
||||
|
@ -42,6 +43,7 @@ type EgressInfo struct {
|
|||
// EgressNetworkRoutes - struct for egress network routes for adding routes to peer's interface
|
||||
type EgressNetworkRoutes struct {
|
||||
NodeAddr net.IPNet `json:"node_addr"`
|
||||
NodeAddr6 net.IPNet `json:"node_addr6"`
|
||||
EgressRanges []string `json:"egress_ranges"`
|
||||
}
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ func (extPeer *ExtClient) AddressIPNet4() net.IPNet {
|
|||
// ExtClient.AddressIPNet6 - return ipv6 IPNet format
|
||||
func (extPeer *ExtClient) AddressIPNet6() net.IPNet {
|
||||
return net.IPNet{
|
||||
IP: net.ParseIP(extPeer.Address),
|
||||
IP: net.ParseIP(extPeer.Address6),
|
||||
Mask: net.CIDRMask(128, 128),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -243,6 +243,7 @@ type HostPull struct {
|
|||
ChangeDefaultGw bool `json:"change_default_gw"`
|
||||
DefaultGwIp net.IP `json:"default_gw_ip"`
|
||||
IsInternetGw bool `json:"is_inet_gw"`
|
||||
EndpointDetection bool `json:"endpoint_detection"`
|
||||
}
|
||||
|
||||
type DefaultGwInfo struct {
|
||||
|
|
141
mq/emqx_cloud.go
141
mq/emqx_cloud.go
|
@ -22,13 +22,6 @@ type userCreateReq struct {
|
|||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type cloudAcl struct {
|
||||
UserName string `json:"username"`
|
||||
Topic string `json:"topic"`
|
||||
Action string `json:"action"`
|
||||
Access string `json:"access"`
|
||||
}
|
||||
|
||||
func (e *EmqxCloud) GetType() servercfg.Emqxdeploy { return servercfg.EmqxCloudDeploy }
|
||||
|
||||
func (e *EmqxCloud) CreateEmqxUser(username, pass string) error {
|
||||
|
@ -89,54 +82,7 @@ func (e *EmqxCloud) CreateEmqxUserforServer() error {
|
|||
if res.StatusCode != http.StatusOK {
|
||||
return errors.New("request failed " + string(body))
|
||||
}
|
||||
// add acls
|
||||
acls := []cloudAcl{
|
||||
{
|
||||
UserName: servercfg.GetMqUserName(),
|
||||
Topic: fmt.Sprintf("update/%s/#", servercfg.GetServer()),
|
||||
Access: "allow",
|
||||
Action: "sub",
|
||||
},
|
||||
{
|
||||
UserName: servercfg.GetMqUserName(),
|
||||
Topic: fmt.Sprintf("host/serverupdate/%s/#", servercfg.GetServer()),
|
||||
Access: "allow",
|
||||
Action: "sub",
|
||||
},
|
||||
{
|
||||
UserName: servercfg.GetMqUserName(),
|
||||
Topic: fmt.Sprintf("signal/%s/#", servercfg.GetServer()),
|
||||
Access: "allow",
|
||||
Action: "sub",
|
||||
},
|
||||
{
|
||||
UserName: servercfg.GetMqUserName(),
|
||||
Topic: fmt.Sprintf("metrics/%s/#", servercfg.GetServer()),
|
||||
Access: "allow",
|
||||
Action: "sub",
|
||||
},
|
||||
{
|
||||
UserName: servercfg.GetMqUserName(),
|
||||
Topic: "peers/host/#",
|
||||
Access: "allow",
|
||||
Action: "pub",
|
||||
},
|
||||
{
|
||||
UserName: servercfg.GetMqUserName(),
|
||||
Topic: "node/update/#",
|
||||
Access: "allow",
|
||||
Action: "pub",
|
||||
},
|
||||
{
|
||||
|
||||
UserName: servercfg.GetMqUserName(),
|
||||
Topic: "host/update/#",
|
||||
Access: "allow",
|
||||
Action: "pub",
|
||||
},
|
||||
}
|
||||
|
||||
return e.createacls(acls)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmqxCloud) CreateEmqxDefaultAuthenticator() error { return nil } // ignore
|
||||
|
@ -147,94 +93,13 @@ func (e *EmqxCloud) CreateDefaultDenyRule() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (e *EmqxCloud) createacls(acls []cloudAcl) error {
|
||||
payload, err := json.Marshal(acls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/acl", e.URL), strings.NewReader(string(payload)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.SetBasicAuth(e.AppID, e.AppSecret)
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return errors.New("request failed " + string(body))
|
||||
}
|
||||
func (e *EmqxCloud) CreateHostACL(hostID, serverName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmqxCloud) CreateHostACL(hostID, serverName string) error {
|
||||
acls := []cloudAcl{
|
||||
{
|
||||
UserName: hostID,
|
||||
Topic: fmt.Sprintf("peers/host/%s/%s", hostID, serverName),
|
||||
Access: "allow",
|
||||
Action: "sub",
|
||||
},
|
||||
{
|
||||
UserName: hostID,
|
||||
Topic: fmt.Sprintf("host/update/%s/%s", hostID, serverName),
|
||||
Access: "allow",
|
||||
Action: "sub",
|
||||
},
|
||||
{
|
||||
UserName: hostID,
|
||||
Topic: fmt.Sprintf("host/serverupdate/%s/%s", serverName, hostID),
|
||||
Access: "allow",
|
||||
Action: "pub",
|
||||
},
|
||||
}
|
||||
|
||||
return e.createacls(acls)
|
||||
}
|
||||
|
||||
func (e *EmqxCloud) AppendNodeUpdateACL(hostID, nodeNetwork, nodeID, serverName string) error {
|
||||
acls := []cloudAcl{
|
||||
{
|
||||
UserName: hostID,
|
||||
Topic: fmt.Sprintf("node/update/%s/%s", nodeNetwork, nodeID),
|
||||
Access: "allow",
|
||||
Action: "sub",
|
||||
},
|
||||
{
|
||||
UserName: hostID,
|
||||
Topic: fmt.Sprintf("ping/%s/%s", serverName, nodeID),
|
||||
Access: "allow",
|
||||
Action: "pubsub",
|
||||
},
|
||||
{
|
||||
UserName: hostID,
|
||||
Topic: fmt.Sprintf("update/%s/%s", serverName, nodeID),
|
||||
Access: "allow",
|
||||
Action: "pubsub",
|
||||
},
|
||||
{
|
||||
UserName: hostID,
|
||||
Topic: fmt.Sprintf("signal/%s/%s", serverName, nodeID),
|
||||
Access: "allow",
|
||||
Action: "pubsub",
|
||||
},
|
||||
{
|
||||
UserName: hostID,
|
||||
Topic: fmt.Sprintf("metrics/%s/%s", serverName, nodeID),
|
||||
Access: "allow",
|
||||
Action: "pubsub",
|
||||
},
|
||||
}
|
||||
return nil
|
||||
|
||||
return e.createacls(acls)
|
||||
}
|
||||
|
||||
func (e *EmqxCloud) GetUserACL(username string) (*aclObject, error) { return nil, nil } // ununsed on cloud since it doesn't overwrite acls list
|
||||
|
|
|
@ -92,7 +92,7 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
|
|||
}
|
||||
decrypted, decryptErr := decryptMsgWithHost(currentHost, msg.Payload())
|
||||
if decryptErr != nil {
|
||||
slog.Error("failed to decrypt message for host", "id", id, "error", decryptErr)
|
||||
slog.Error("failed to decrypt message for host", "id", id, "name", currentHost.Name, "error", decryptErr)
|
||||
return
|
||||
}
|
||||
var hostUpdate models.HostUpdate
|
||||
|
@ -198,7 +198,21 @@ func signalPeer(signal models.Signal) {
|
|||
signal.IsPro = servercfg.IsPro
|
||||
peerHost, err := logic.GetHost(signal.ToHostID)
|
||||
if err != nil {
|
||||
slog.Error("failed to signal, peer not found", "error", err)
|
||||
slog.Error("failed to signal, peer host not found", "error", err)
|
||||
return
|
||||
}
|
||||
peerNode, err := logic.GetNodeByID(signal.ToNodeID)
|
||||
if err != nil {
|
||||
slog.Error("failed to signal, node not found", "error", err)
|
||||
return
|
||||
}
|
||||
node, err := logic.GetNodeByID(signal.FromNodeID)
|
||||
if err != nil {
|
||||
slog.Error("failed to signal, peer node not found", "error", err)
|
||||
return
|
||||
}
|
||||
if peerNode.IsIngressGateway || node.IsIngressGateway || peerNode.IsInternetGateway || node.IsInternetGateway {
|
||||
signal.Action = ""
|
||||
return
|
||||
}
|
||||
err = HostUpdate(&models.HostUpdate{
|
||||
|
@ -282,9 +296,11 @@ func HandleHostCheckin(h, currentHost *models.Host) bool {
|
|||
!h.EndpointIP.Equal(currentHost.EndpointIP) ||
|
||||
(len(h.NatType) > 0 && h.NatType != currentHost.NatType) ||
|
||||
h.DefaultInterface != currentHost.DefaultInterface ||
|
||||
(h.ListenPort != 0 && h.ListenPort != currentHost.ListenPort) || (h.WgPublicListenPort != 0 && h.WgPublicListenPort != currentHost.WgPublicListenPort)
|
||||
(h.ListenPort != 0 && h.ListenPort != currentHost.ListenPort) ||
|
||||
(h.WgPublicListenPort != 0 && h.WgPublicListenPort != currentHost.WgPublicListenPort) || (!h.EndpointIPv6.Equal(currentHost.EndpointIPv6))
|
||||
if ifaceDelta { // only save if something changes
|
||||
currentHost.EndpointIP = h.EndpointIP
|
||||
currentHost.EndpointIPv6 = h.EndpointIPv6
|
||||
currentHost.Interfaces = h.Interfaces
|
||||
currentHost.DefaultInterface = h.DefaultInterface
|
||||
currentHost.NatType = h.NatType
|
||||
|
|
27
mq/mq.go
27
mq/mq.go
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/gravitl/netmaker/logger"
|
||||
"github.com/gravitl/netmaker/logic"
|
||||
"github.com/gravitl/netmaker/servercfg"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// KEEPALIVE_TIMEOUT - time in seconds for timeout
|
||||
|
@ -27,19 +28,20 @@ var mqclient mqtt.Client
|
|||
func setMqOptions(user, password string, opts *mqtt.ClientOptions) {
|
||||
broker, _ := servercfg.GetMessageQueueEndpoint()
|
||||
opts.AddBroker(broker)
|
||||
id := logic.RandomString(23)
|
||||
opts.ClientID = id
|
||||
opts.ClientID = logic.RandomString(23)
|
||||
opts.SetUsername(user)
|
||||
opts.SetPassword(password)
|
||||
opts.SetAutoReconnect(true)
|
||||
opts.SetConnectRetry(true)
|
||||
opts.SetConnectRetryInterval(time.Second << 2)
|
||||
opts.SetCleanSession(true)
|
||||
opts.SetConnectRetryInterval(time.Second * 4)
|
||||
opts.SetKeepAlive(time.Minute)
|
||||
opts.SetCleanSession(true)
|
||||
opts.SetWriteTimeout(time.Minute)
|
||||
}
|
||||
|
||||
// SetupMQTT creates a connection to broker and return client
|
||||
func SetupMQTT() {
|
||||
func SetupMQTT(fatal bool) {
|
||||
if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
|
||||
if emqx.GetType() == servercfg.EmqxOnPremDeploy {
|
||||
time.Sleep(10 * time.Second) // wait for the REST endpoint to be ready
|
||||
|
@ -69,6 +71,7 @@ func SetupMQTT() {
|
|||
|
||||
opts := mqtt.NewClientOptions()
|
||||
setMqOptions(servercfg.GetMqUserName(), servercfg.GetMqPassword(), opts)
|
||||
logger.Log(0, "Mq Client Connecting with Random ID: ", opts.ClientID)
|
||||
opts.SetOnConnectHandler(func(client mqtt.Client) {
|
||||
serverName := servercfg.GetServer()
|
||||
if token := client.Subscribe(fmt.Sprintf("update/%s/#", serverName), 0, mqtt.MessageHandler(UpdateNode)); token.WaitTimeout(MQ_TIMEOUT*time.Second) && token.Error() != nil {
|
||||
|
@ -91,6 +94,13 @@ func SetupMQTT() {
|
|||
opts.SetOrderMatters(false)
|
||||
opts.SetResumeSubs(true)
|
||||
})
|
||||
opts.SetConnectionLostHandler(func(c mqtt.Client, e error) {
|
||||
slog.Warn("detected broker connection lost", "err", e.Error())
|
||||
c.Disconnect(250)
|
||||
slog.Info("re-initiating MQ connection")
|
||||
SetupMQTT(false)
|
||||
|
||||
})
|
||||
mqclient = mqtt.NewClient(opts)
|
||||
tperiod := time.Now().Add(10 * time.Second)
|
||||
for {
|
||||
|
@ -98,10 +108,17 @@ func SetupMQTT() {
|
|||
logger.Log(2, "unable to connect to broker, retrying ...")
|
||||
if time.Now().After(tperiod) {
|
||||
if token.Error() == nil {
|
||||
if fatal {
|
||||
logger.FatalLog("could not connect to broker, token timeout, exiting ...")
|
||||
}
|
||||
logger.Log(0, "could not connect to broker, token timeout, exiting ...")
|
||||
|
||||
} else {
|
||||
if fatal {
|
||||
logger.FatalLog("could not connect to broker, exiting ...", token.Error().Error())
|
||||
}
|
||||
logger.Log(0, "could not connect to broker, exiting ...", token.Error().Error())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break
|
||||
|
@ -124,7 +141,7 @@ func Keepalive(ctx context.Context) {
|
|||
|
||||
// IsConnected - function for determining if the mqclient is connected or not
|
||||
func IsConnected() bool {
|
||||
return mqclient != nil && mqclient.IsConnected()
|
||||
return mqclient != nil && mqclient.IsConnectionOpen()
|
||||
}
|
||||
|
||||
// CloseClient - function to close the mq connection from server
|
||||
|
|
|
@ -134,10 +134,15 @@ func failOverME(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
host, err := logic.GetHost(node.HostID.String())
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
|
||||
failOverNode, exists := proLogic.FailOverExists(node.Network)
|
||||
if !exists {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failover node doesn't exist in the network"), "badrequest"))
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("req-from: %s, failover node doesn't exist in the network", host.Name), "badrequest"))
|
||||
return
|
||||
}
|
||||
var failOverReq models.FailOverMeReq
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/gravitl/netmaker/models"
|
||||
"github.com/gravitl/netmaker/mq"
|
||||
proLogic "github.com/gravitl/netmaker/pro/logic"
|
||||
"github.com/gravitl/netmaker/servercfg"
|
||||
)
|
||||
|
||||
// InetHandlers - handlers for internet gw
|
||||
|
@ -66,6 +67,14 @@ func createInternetGw(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
proLogic.SetInternetGw(&node, request)
|
||||
if servercfg.IsPro {
|
||||
if _, exists := proLogic.FailOverExists(node.Network); exists {
|
||||
go func() {
|
||||
proLogic.ResetFailedOverPeer(&node)
|
||||
mq.PublishPeerUpdate(false)
|
||||
}()
|
||||
}
|
||||
}
|
||||
err = logic.UpsertNode(&node)
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
|
|
|
@ -2,9 +2,11 @@ package controllers
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
proLogic "github.com/gravitl/netmaker/pro/logic"
|
||||
"net/http"
|
||||
|
||||
proLogic "github.com/gravitl/netmaker/pro/logic"
|
||||
"golang.org/x/exp/slog"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gravitl/netmaker/database"
|
||||
"github.com/gravitl/netmaker/logger"
|
||||
|
@ -122,12 +124,13 @@ func getNetworkExtMetrics(w http.ResponseWriter, r *http.Request) {
|
|||
continue
|
||||
}
|
||||
// if metrics for that client have been reported, append them
|
||||
if len(ingressMetrics.Connectivity[clients[j].ClientID].NodeName) > 0 {
|
||||
if _, ok := ingressMetrics.Connectivity[clients[j].ClientID]; ok {
|
||||
networkMetrics.Connectivity[clients[j].ClientID] = ingressMetrics.Connectivity[clients[j].ClientID]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slog.Debug("sending collected client metrics", "metrics", networkMetrics.Connectivity)
|
||||
logger.Log(1, r.Header.Get("user"), "fetched ext client metrics for network", network)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(networkMetrics.Connectivity)
|
||||
|
|
|
@ -1,17 +1,26 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gravitl/netmaker/logic"
|
||||
"github.com/gravitl/netmaker/servercfg"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var limitedApis = map[string]struct{}{
|
||||
"/api/server/status": {},
|
||||
"/api/emqx/hosts": {},
|
||||
"/api/users/adm/authenticate": {},
|
||||
}
|
||||
|
||||
func OnlyServerAPIWhenUnlicensedMiddleware(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
if servercfg.ErrLicenseValidation != nil && request.URL.Path != "/api/server/status" {
|
||||
if servercfg.ErrLicenseValidation != nil {
|
||||
if _, ok := limitedApis[request.URL.Path]; !ok {
|
||||
logic.ReturnErrorResponse(writer, request, logic.FormatError(servercfg.ErrLicenseValidation, "forbidden"))
|
||||
return
|
||||
}
|
||||
}
|
||||
handler.ServeHTTP(writer, request)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ func InitPro() {
|
|||
})
|
||||
logic.ResetFailOver = proLogic.ResetFailOver
|
||||
logic.ResetFailedOverPeer = proLogic.ResetFailedOverPeer
|
||||
logic.FailOverExists = proLogic.FailOverExists
|
||||
logic.CreateFailOver = proLogic.CreateFailOver
|
||||
logic.GetFailOverPeerIps = proLogic.GetFailOverPeerIps
|
||||
logic.DenyClientNodeAccess = proLogic.DenyClientNode
|
||||
|
|
|
@ -11,6 +11,9 @@ import (
|
|||
)
|
||||
|
||||
func SetFailOverCtx(failOverNode, victimNode, peerNode models.Node) error {
|
||||
if victimNode.IsIngressGateway || peerNode.IsIngressGateway || victimNode.IsInternetGateway || peerNode.IsInternetGateway {
|
||||
return nil
|
||||
}
|
||||
if peerNode.FailOverPeers == nil {
|
||||
peerNode.FailOverPeers = make(map[string]struct{})
|
||||
}
|
||||
|
@ -119,6 +122,9 @@ func GetFailOverPeerIps(peer, node *models.Node) []net.IPNet {
|
|||
}
|
||||
allowedips = append(allowedips, allowed)
|
||||
}
|
||||
if failOverpeer.IsEgressGateway {
|
||||
allowedips = append(allowedips, logic.GetEgressIPs(&failOverpeer)...)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,6 @@ func MQUpdateMetrics(client mqtt.Client, msg mqtt.Message) {
|
|||
}
|
||||
|
||||
func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) {
|
||||
|
||||
oldMetrics, err := logic.GetMetrics(currentNode.ID.String())
|
||||
if err != nil {
|
||||
slog.Error("error finding old metrics for node", "id", currentNode.ID, "error", err)
|
||||
|
@ -121,21 +120,13 @@ func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) {
|
|||
if newMetrics.Connectivity == nil {
|
||||
newMetrics.Connectivity = make(map[string]models.Metric)
|
||||
}
|
||||
if len(attachedClients) > 0 {
|
||||
// associate ext clients with IDs
|
||||
for i := range attachedClients {
|
||||
extMetric := newMetrics.Connectivity[attachedClients[i].PublicKey]
|
||||
if len(extMetric.NodeName) == 0 &&
|
||||
len(newMetrics.Connectivity[attachedClients[i].ClientID].NodeName) > 0 { // cover server clients
|
||||
extMetric = newMetrics.Connectivity[attachedClients[i].ClientID]
|
||||
if extMetric.TotalReceived > 0 && extMetric.TotalSent > 0 {
|
||||
extMetric.Connected = true
|
||||
}
|
||||
}
|
||||
extMetric.NodeName = attachedClients[i].ClientID
|
||||
slog.Debug("[metrics] processing attached client", "client", attachedClients[i].ClientID, "public key", attachedClients[i].PublicKey)
|
||||
clientMetric := newMetrics.Connectivity[attachedClients[i].PublicKey]
|
||||
clientMetric.NodeName = attachedClients[i].ClientID
|
||||
newMetrics.Connectivity[attachedClients[i].ClientID] = clientMetric
|
||||
delete(newMetrics.Connectivity, attachedClients[i].PublicKey)
|
||||
newMetrics.Connectivity[attachedClients[i].ClientID] = extMetric
|
||||
}
|
||||
slog.Debug("[metrics] attached client metric", "metric", clientMetric)
|
||||
}
|
||||
|
||||
// run through metrics for each peer
|
||||
|
@ -168,7 +159,5 @@ func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) {
|
|||
|
||||
}
|
||||
|
||||
for k := range oldMetrics.Connectivity { // cleanup any left over data, self healing
|
||||
delete(newMetrics.Connectivity, k)
|
||||
}
|
||||
slog.Debug("[metrics] node metrics data", "node ID", currentNode.ID, "metrics", newMetrics)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,11 @@ import (
|
|||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
const (
|
||||
IPv4Network = "0.0.0.0/0"
|
||||
IPv6Network = "::/0"
|
||||
)
|
||||
|
||||
func ValidateInetGwReq(inetNode models.Node, req models.InetNodeReq, update bool) error {
|
||||
inetHost, err := logic.GetHost(inetNode.HostID.String())
|
||||
if err != nil {
|
||||
|
@ -104,6 +109,9 @@ func SetDefaultGwForRelayedUpdate(relayed, relay models.Node, peerUpdate models.
|
|||
if relay.InternetGwID != "" {
|
||||
peerUpdate.ChangeDefaultGw = true
|
||||
peerUpdate.DefaultGwIp = relay.Address.IP
|
||||
if peerUpdate.DefaultGwIp == nil {
|
||||
peerUpdate.DefaultGwIp = relay.Address6.IP
|
||||
}
|
||||
|
||||
}
|
||||
return peerUpdate
|
||||
|
@ -118,7 +126,9 @@ func SetDefaultGw(node models.Node, peerUpdate models.HostPeerUpdate) models.Hos
|
|||
}
|
||||
peerUpdate.ChangeDefaultGw = true
|
||||
peerUpdate.DefaultGwIp = inetNode.Address.IP
|
||||
|
||||
if peerUpdate.DefaultGwIp == nil {
|
||||
peerUpdate.DefaultGwIp = inetNode.Address6.IP
|
||||
}
|
||||
}
|
||||
return peerUpdate
|
||||
}
|
||||
|
@ -140,6 +150,18 @@ func GetNetworkIngresses(network string) ([]models.Node, error) {
|
|||
|
||||
// GetAllowedIpsForInet - get inet cidr for node using a inet gw
|
||||
func GetAllowedIpForInetNodeClient(node, peer *models.Node) []net.IPNet {
|
||||
_, ipnet, _ := net.ParseCIDR("0.0.0.0/0")
|
||||
return []net.IPNet{*ipnet}
|
||||
var allowedips = []net.IPNet{}
|
||||
|
||||
if peer.Address.IP != nil {
|
||||
_, ipnet, _ := net.ParseCIDR(IPv4Network)
|
||||
allowedips = append(allowedips, *ipnet)
|
||||
return allowedips
|
||||
}
|
||||
|
||||
if peer.Address6.IP != nil {
|
||||
_, ipnet, _ := net.ParseCIDR(IPv6Network)
|
||||
allowedips = append(allowedips, *ipnet)
|
||||
}
|
||||
|
||||
return allowedips
|
||||
}
|
||||
|
|
|
@ -238,7 +238,7 @@ func getRelayedAddresses(id string) []net.IPNet {
|
|||
addrs = append(addrs, node.Address)
|
||||
}
|
||||
if node.Address6.IP != nil {
|
||||
node.Address.Mask = net.CIDRMask(128, 128)
|
||||
node.Address6.Mask = net.CIDRMask(128, 128)
|
||||
addrs = append(addrs, node.Address6)
|
||||
}
|
||||
return addrs
|
||||
|
|
|
@ -42,7 +42,7 @@ const trial_data_key = "trialdata"
|
|||
|
||||
// stores trial end date
|
||||
func initTrial() error {
|
||||
telData, _ := logic.FetchTelemetryData()
|
||||
telData := logic.FetchTelemetryData()
|
||||
if telData.Hosts > 0 || telData.Networks > 0 || telData.Users > 0 {
|
||||
return nil // database is already populated, so skip creating trial
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ type Usage struct {
|
|||
Egresses int `json:"egresses"`
|
||||
Relays int `json:"relays"`
|
||||
InternetGateways int `json:"internet_gateways"`
|
||||
FailOvers int `json:"fail_overs"`
|
||||
}
|
||||
|
||||
// Usage.SetDefaults - sets the default values for usage
|
||||
|
|
|
@ -59,5 +59,9 @@ func getCurrentServerUsage() (limits Usage) {
|
|||
if err == nil {
|
||||
limits.InternetGateways = len(gateways)
|
||||
}
|
||||
failovers, err := logic.GetAllFailOvers()
|
||||
if err == nil {
|
||||
limits.FailOvers = len(failovers)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
27
release.md
27
release.md
|
@ -1,19 +1,24 @@
|
|||
# Netmaker v0.23.0
|
||||
# Netmaker v0.24.0
|
||||
|
||||
## Whats New ✨
|
||||
|
||||
- Revamped Internet Gateways: hosts and clients can now use internet gateways! More info [here](https://docs.netmaker.io/pro/internet-gateways.html)
|
||||
On community edition, internet gateways for clients can be accessed via the Remote Access tab.
|
||||
- PostUp and PostDown commands for clients
|
||||
- EMQX cloud support
|
||||
- Metadata for Remote Access Gateways
|
||||
- IPv6 and Dual Stack Networks Support Across Platform
|
||||
- Endpoint Detection Can Now Be Turned Off By Setting `ENDPOINT_DETECTION=false` On Server Config
|
||||
- New SignUp Flow For Oauth Users, With Admin Approval Process.
|
||||
- Added Failover Commands to nmctl
|
||||
|
||||
## What's Fixed/Improved 🛠
|
||||
|
||||
- Allow creation of gateways, relays and egress without clients, relayed hosts and external ranges respectively
|
||||
- Make default host a remote access gateway and a failover host on joining a new network
|
||||
- Stability fixes with ACLs
|
||||
- Fixed issues with install/upgrade scripts
|
||||
- Fixed issues with CoreDNS
|
||||
- Scalability Fixes around Mq connection, ACLs
|
||||
- Fixed Zombie Node Logic To Avoid Choking On the Channel
|
||||
- Fixed Egress Routes In Dual Stack Netmaker Overlay Networks
|
||||
- Fixed Client Connectivity Metrics Data
|
||||
- Fixed auto-relay with enrollment key
|
||||
- Imporved Logic Around Oauth Sceret Management
|
||||
- Improved Oauth Message Templates
|
||||
|
||||
## Known Issues 🐞
|
||||
|
||||
- Erratic Traffic Data In Metrics
|
||||
- `netclient server leave` Leaves a Stale Node Record In At Least One Network When Part Of Multiple Networks, But Can Be Deleted From The UI.
|
||||
- On Darwin Stale Egress Route Entries Remain On The Machine After Removing Egress Range Or Removing The Egress Server
|
||||
|
|
|
@ -53,6 +53,8 @@ TELEMETRY=on
|
|||
# OAuth section
|
||||
#
|
||||
###
|
||||
# only mentioned domains will be allowded to signup using oauth, by default all domains are allowed
|
||||
ALLOWED_EMAIL_DOMAINS=*
|
||||
# "<azure-ad|github|google|oidc>"
|
||||
AUTH_PROVIDER=
|
||||
# "<client id of your oauth provider>"
|
||||
|
@ -71,3 +73,5 @@ JWT_VALIDITY_DURATION=43200
|
|||
RAC_AUTO_DISABLE=true
|
||||
# if turned on data will be cached on to improve performance significantly (IMPORTANT: If HA set to `false` )
|
||||
CACHING_ENABLED=true
|
||||
# if turned on netclient checks if peers are reachable over private/LAN address, and choose that as peer endpoint
|
||||
ENDPOINT_DETECTION=true
|
||||
|
|
|
@ -248,8 +248,8 @@ save_config() { (
|
|||
local toCopy=("SERVER_HOST" "MASTER_KEY" "MQ_USERNAME" "MQ_PASSWORD"
|
||||
"INSTALL_TYPE" "NODE_ID" "DNS_MODE" "NETCLIENT_AUTO_UPDATE" "API_PORT"
|
||||
"CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "VERBOSITY"
|
||||
"DEBUG_MODE" "REST_BACKEND" "DISABLE_REMOTE_IP_CHECK" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
|
||||
"FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE" "CACHING_ENABLED")
|
||||
"DEBUG_MODE" "REST_BACKEND" "DISABLE_REMOTE_IP_CHECK" "TELEMETRY" "ALLOWED_EMAIL_DOMAINS" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
|
||||
"FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE" "CACHING_ENABLED" "ENDPOINT_DETECTION")
|
||||
for name in "${toCopy[@]}"; do
|
||||
save_config_item $name "${!name}"
|
||||
done
|
||||
|
@ -694,7 +694,12 @@ upgrade() {
|
|||
unset IMAGE_TAG
|
||||
unset BUILD_TAG
|
||||
IMAGE_TAG=$UI_IMAGE_TAG
|
||||
semver=$(chsv_check_version_ex "$UI_IMAGE_TAG")
|
||||
if [[ ! "$semver" ]]; then
|
||||
BUILD_TAG=$LATEST
|
||||
else
|
||||
BUILD_TAG=$UI_IMAGE_TAG
|
||||
fi
|
||||
echo "-----------------------------------------------------"
|
||||
echo "Provide Details for pro installation:"
|
||||
echo " 1. Log into https://app.netmaker.io"
|
||||
|
@ -720,7 +725,13 @@ downgrade () {
|
|||
unset IMAGE_TAG
|
||||
unset BUILD_TAG
|
||||
IMAGE_TAG=$UI_IMAGE_TAG
|
||||
|
||||
semver=$(chsv_check_version_ex "$UI_IMAGE_TAG")
|
||||
if [[ ! "$semver" ]]; then
|
||||
BUILD_TAG=$LATEST
|
||||
else
|
||||
BUILD_TAG=$UI_IMAGE_TAG
|
||||
fi
|
||||
save_config
|
||||
if [ -a "$SCRIPT_DIR"/docker-compose.override.yml ]; then
|
||||
rm -f "$SCRIPT_DIR"/docker-compose.override.yml
|
||||
|
@ -730,6 +741,23 @@ downgrade () {
|
|||
install_netmaker
|
||||
}
|
||||
|
||||
function chsv_check_version() {
|
||||
if [[ $1 =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$ ]]; then
|
||||
echo "$1"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
function chsv_check_version_ex() {
|
||||
if [[ $1 =~ ^v.+$ ]]; then
|
||||
chsv_check_version "${1:1}"
|
||||
else
|
||||
chsv_check_version "${1}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
main (){
|
||||
|
||||
|
|
|
@ -441,7 +441,7 @@ func GetPublicIP() (string, error) {
|
|||
endpoint := ""
|
||||
var err error
|
||||
|
||||
iplist := []string{"https://ip.server.gravitl.com", "https://ifconfig.me", "https://api.ipify.org", "https://ipinfo.io/ip"}
|
||||
iplist := []string{"https://ifconfig.me", "https://api.ipify.org", "https://ipinfo.io/ip"}
|
||||
publicIpService := os.Getenv("PUBLIC_IP_SERVICE")
|
||||
if publicIpService != "" {
|
||||
// prepend the user-specified service so it's checked first
|
||||
|
@ -674,6 +674,15 @@ func DeployedByOperator() bool {
|
|||
return config.Config.Server.DeployedByOperator
|
||||
}
|
||||
|
||||
// IsEndpointDetectionEnabled - returns true if endpoint detection enabled
|
||||
func IsEndpointDetectionEnabled() bool {
|
||||
var enabled = true //default
|
||||
if os.Getenv("ENDPOINT_DETECTION") != "" {
|
||||
enabled = os.Getenv("ENDPOINT_DETECTION") == "true"
|
||||
}
|
||||
return enabled
|
||||
}
|
||||
|
||||
// GetEnvironment returns the environment the server is running in (e.g. dev, staging, prod...)
|
||||
func GetEnvironment() string {
|
||||
if env := os.Getenv("ENVIRONMENT"); env != "" {
|
||||
|
@ -703,3 +712,14 @@ func GetEmqxAppID() string {
|
|||
func GetEmqxAppSecret() string {
|
||||
return os.Getenv("EMQX_APP_SECRET")
|
||||
}
|
||||
|
||||
// GetAllowedEmailDomains - gets the allowed email domains for oauth signup
|
||||
func GetAllowedEmailDomains() string {
|
||||
allowedDomains := "*"
|
||||
if os.Getenv("ALLOWED_EMAIL_DOMAINS") != "" {
|
||||
allowedDomains = os.Getenv("ALLOWED_EMAIL_DOMAINS")
|
||||
} else if config.Config.Server.AllowedEmailDomains != "" {
|
||||
allowedDomains = config.Config.Server.AllowedEmailDomains
|
||||
}
|
||||
return allowedDomains
|
||||
}
|
||||
|
|
567
swagger.yml
567
swagger.yml
|
@ -15,9 +15,43 @@ definitions:
|
|||
description: ACLContainer - the total list of all node's ACL in a given network
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/logic/acls
|
||||
APIEnrollmentKey:
|
||||
description: APIEnrollmentKey - used to create enrollment keys via API
|
||||
properties:
|
||||
expiration:
|
||||
format: int64
|
||||
type: integer
|
||||
x-go-name: Expiration
|
||||
networks:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: Networks
|
||||
relay:
|
||||
type: string
|
||||
x-go-name: Relay
|
||||
tags:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: Tags
|
||||
type:
|
||||
$ref: '#/definitions/KeyType'
|
||||
unlimited:
|
||||
type: boolean
|
||||
x-go-name: Unlimited
|
||||
uses_remaining:
|
||||
format: int64
|
||||
type: integer
|
||||
x-go-name: UsesRemaining
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
ApiHost:
|
||||
description: ApiHost - the host struct for API usage
|
||||
properties:
|
||||
autoupdate:
|
||||
type: boolean
|
||||
x-go-name: AutoUpdate
|
||||
debug:
|
||||
type: boolean
|
||||
x-go-name: Debug
|
||||
|
@ -35,18 +69,12 @@ definitions:
|
|||
x-go-name: ID
|
||||
interfaces:
|
||||
items:
|
||||
$ref: '#/definitions/Iface'
|
||||
$ref: '#/definitions/ApiIface'
|
||||
type: array
|
||||
x-go-name: Interfaces
|
||||
isdefault:
|
||||
type: boolean
|
||||
x-go-name: IsDefault
|
||||
isrelay:
|
||||
type: boolean
|
||||
x-go-name: IsRelay
|
||||
isrelayed:
|
||||
type: boolean
|
||||
x-go-name: IsRelayed
|
||||
isstatic:
|
||||
type: boolean
|
||||
x-go-name: IsStatic
|
||||
|
@ -75,17 +103,13 @@ definitions:
|
|||
os:
|
||||
type: string
|
||||
x-go-name: OS
|
||||
persistentkeepalive:
|
||||
format: int64
|
||||
type: integer
|
||||
x-go-name: PersistentKeepalive
|
||||
publickey:
|
||||
type: string
|
||||
x-go-name: PublicKey
|
||||
relay_hosts:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: RelayedHosts
|
||||
relayed_by:
|
||||
type: string
|
||||
x-go-name: RelayedBy
|
||||
verbosity:
|
||||
format: int64
|
||||
type: integer
|
||||
|
@ -99,6 +123,139 @@ definitions:
|
|||
x-go-name: WgPublicListenPort
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
ApiIface:
|
||||
description: |-
|
||||
ApiIface - the interface struct for API usage
|
||||
The original Iface struct contains a net.Address, which does not get marshalled correctly
|
||||
properties:
|
||||
addressString:
|
||||
type: string
|
||||
x-go-name: AddressString
|
||||
name:
|
||||
type: string
|
||||
x-go-name: Name
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
ApiNode:
|
||||
description: ApiNode is a stripped down Node DTO that exposes only required fields to external systems
|
||||
properties:
|
||||
address:
|
||||
type: string
|
||||
x-go-name: Address
|
||||
address6:
|
||||
type: string
|
||||
x-go-name: Address6
|
||||
allowedips:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: AllowedIPs
|
||||
connected:
|
||||
type: boolean
|
||||
x-go-name: Connected
|
||||
defaultacl:
|
||||
description: == PRO ==
|
||||
type: string
|
||||
x-go-name: DefaultACL
|
||||
dnson:
|
||||
type: boolean
|
||||
x-go-name: DNSOn
|
||||
egressgatewaynatenabled:
|
||||
type: boolean
|
||||
x-go-name: EgressGatewayNatEnabled
|
||||
egressgatewayranges:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: EgressGatewayRanges
|
||||
expdatetime:
|
||||
format: int64
|
||||
type: integer
|
||||
x-go-name: ExpirationDateTime
|
||||
fail_over_peers:
|
||||
additionalProperties:
|
||||
type: object
|
||||
type: object
|
||||
x-go-name: FailOverPeers
|
||||
failed_over_by:
|
||||
format: uuid
|
||||
type: string
|
||||
x-go-name: FailedOverBy
|
||||
hostid:
|
||||
type: string
|
||||
x-go-name: HostID
|
||||
id:
|
||||
type: string
|
||||
x-go-name: ID
|
||||
inet_node_req:
|
||||
$ref: '#/definitions/InetNodeReq'
|
||||
ingressdns:
|
||||
type: string
|
||||
x-go-name: IngressDns
|
||||
internetgw_node_id:
|
||||
type: string
|
||||
x-go-name: InternetGwID
|
||||
is_fail_over:
|
||||
type: boolean
|
||||
x-go-name: IsFailOver
|
||||
isegressgateway:
|
||||
type: boolean
|
||||
x-go-name: IsEgressGateway
|
||||
isingressgateway:
|
||||
type: boolean
|
||||
x-go-name: IsIngressGateway
|
||||
isinternetgateway:
|
||||
type: boolean
|
||||
x-go-name: IsInternetGateway
|
||||
isrelay:
|
||||
type: boolean
|
||||
x-go-name: IsRelay
|
||||
isrelayed:
|
||||
type: boolean
|
||||
x-go-name: IsRelayed
|
||||
lastcheckin:
|
||||
format: int64
|
||||
type: integer
|
||||
x-go-name: LastCheckIn
|
||||
lastmodified:
|
||||
format: int64
|
||||
type: integer
|
||||
x-go-name: LastModified
|
||||
lastpeerupdate:
|
||||
format: int64
|
||||
type: integer
|
||||
x-go-name: LastPeerUpdate
|
||||
localaddress:
|
||||
type: string
|
||||
x-go-name: LocalAddress
|
||||
metadata:
|
||||
type: string
|
||||
x-go-name: Metadata
|
||||
network:
|
||||
type: string
|
||||
x-go-name: Network
|
||||
networkrange:
|
||||
type: string
|
||||
x-go-name: NetworkRange
|
||||
networkrange6:
|
||||
type: string
|
||||
x-go-name: NetworkRange6
|
||||
pendingdelete:
|
||||
type: boolean
|
||||
x-go-name: PendingDelete
|
||||
relayedby:
|
||||
type: string
|
||||
x-go-name: RelayedBy
|
||||
relaynodes:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: RelayedNodes
|
||||
server:
|
||||
type: string
|
||||
x-go-name: Server
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
AuthParams:
|
||||
description: AuthParams - struct for auth params
|
||||
properties:
|
||||
|
@ -135,6 +292,12 @@ definitions:
|
|||
type: string
|
||||
type: array
|
||||
x-go-name: ExtraAllowedIPs
|
||||
postdown:
|
||||
type: string
|
||||
x-go-name: PostDown
|
||||
postup:
|
||||
type: string
|
||||
x-go-name: PostUp
|
||||
publickey:
|
||||
type: string
|
||||
x-go-name: PublicKey
|
||||
|
@ -187,6 +350,32 @@ definitions:
|
|||
x-go-name: Ranges
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
EgressInfo:
|
||||
description: EgressInfo - struct for egress info
|
||||
properties:
|
||||
egress_gateway_cfg:
|
||||
$ref: '#/definitions/EgressGatewayRequest'
|
||||
egress_gw_addr:
|
||||
$ref: '#/definitions/IPNet'
|
||||
egress_id:
|
||||
type: string
|
||||
x-go-name: EgressID
|
||||
network:
|
||||
$ref: '#/definitions/IPNet'
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
EgressNetworkRoutes:
|
||||
description: EgressNetworkRoutes - struct for egress network routes for adding routes to peer's interface
|
||||
properties:
|
||||
egress_ranges:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: EgressRanges
|
||||
node_addr:
|
||||
$ref: '#/definitions/IPNet'
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
EnrollmentKey:
|
||||
description: EnrollmentKey - the key used to register hosts and join them to specific networks
|
||||
properties:
|
||||
|
@ -199,6 +388,10 @@ definitions:
|
|||
type: string
|
||||
type: array
|
||||
x-go-name: Networks
|
||||
relay:
|
||||
format: uuid
|
||||
type: string
|
||||
x-go-name: Relay
|
||||
tags:
|
||||
items:
|
||||
type: string
|
||||
|
@ -230,6 +423,11 @@ definitions:
|
|||
address6:
|
||||
type: string
|
||||
x-go-name: Address6
|
||||
allowed_ips:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: AllowedIPs
|
||||
clientid:
|
||||
type: string
|
||||
x-go-name: ClientID
|
||||
|
@ -265,6 +463,12 @@ definitions:
|
|||
ownerid:
|
||||
type: string
|
||||
x-go-name: OwnerID
|
||||
postdown:
|
||||
type: string
|
||||
x-go-name: PostDown
|
||||
postup:
|
||||
type: string
|
||||
x-go-name: PostUp
|
||||
privatekey:
|
||||
type: string
|
||||
x-go-name: PrivateKey
|
||||
|
@ -280,6 +484,19 @@ definitions:
|
|||
title: File represents an open file descriptor.
|
||||
type: object
|
||||
x-go-package: os
|
||||
FwUpdate:
|
||||
description: FwUpdate - struct for firewall updates
|
||||
properties:
|
||||
egress_info:
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/EgressInfo'
|
||||
type: object
|
||||
x-go-name: EgressInfo
|
||||
is_egress_gw:
|
||||
type: boolean
|
||||
x-go-name: IsEgressGw
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
HardwareAddr:
|
||||
items:
|
||||
format: uint8
|
||||
|
@ -362,6 +579,8 @@ definitions:
|
|||
os:
|
||||
type: string
|
||||
x-go-name: OS
|
||||
persistentkeepalive:
|
||||
$ref: '#/definitions/Duration'
|
||||
publickey:
|
||||
$ref: '#/definitions/Key'
|
||||
traffickeypublic:
|
||||
|
@ -386,11 +605,55 @@ definitions:
|
|||
x-go-name: WgPublicListenPort
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
HostInfoMap:
|
||||
additionalProperties:
|
||||
$ref: '#/definitions/HostNetworkInfo'
|
||||
description: HostInfoMap - map of host public keys to host networking info
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
HostNetworkInfo:
|
||||
description: HostNetworkInfo - holds info related to host networking (used for client side peer calculations)
|
||||
properties:
|
||||
interfaces:
|
||||
items:
|
||||
$ref: '#/definitions/Iface'
|
||||
type: array
|
||||
x-go-name: Interfaces
|
||||
is_static:
|
||||
type: boolean
|
||||
x-go-name: IsStatic
|
||||
listen_port:
|
||||
format: int64
|
||||
type: integer
|
||||
x-go-name: ListenPort
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
HostPull:
|
||||
description: HostPull - response of a host's pull
|
||||
properties:
|
||||
change_default_gw:
|
||||
type: boolean
|
||||
x-go-name: ChangeDefaultGw
|
||||
default_gw_ip:
|
||||
type: string
|
||||
x-go-name: DefaultGwIp
|
||||
egress_network_routes:
|
||||
items:
|
||||
$ref: '#/definitions/EgressNetworkRoutes'
|
||||
type: array
|
||||
x-go-name: EgressRoutes
|
||||
endpoint_detection:
|
||||
type: boolean
|
||||
x-go-name: EndpointDetection
|
||||
fw_update:
|
||||
$ref: '#/definitions/FwUpdate'
|
||||
host:
|
||||
$ref: '#/definitions/Host'
|
||||
host_network_info:
|
||||
$ref: '#/definitions/HostInfoMap'
|
||||
is_inet_gw:
|
||||
type: boolean
|
||||
x-go-name: IsInternetGw
|
||||
nodes:
|
||||
items:
|
||||
$ref: '#/definitions/Node'
|
||||
|
@ -413,6 +676,9 @@ definitions:
|
|||
address:
|
||||
type: string
|
||||
x-go-name: Address
|
||||
host_id:
|
||||
type: string
|
||||
x-go-name: HostID
|
||||
id:
|
||||
type: string
|
||||
x-go-name: ID
|
||||
|
@ -466,6 +732,16 @@ definitions:
|
|||
x-go-name: Name
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
InetNodeReq:
|
||||
description: InetNodeReq - exit node request struct
|
||||
properties:
|
||||
inet_node_client_ids:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: InetNodeClientIDs
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
Key:
|
||||
description: |-
|
||||
A Key is a public, private, or pre-shared secret key. The Key constructor
|
||||
|
@ -758,13 +1034,15 @@ definitions:
|
|||
format: date-time
|
||||
type: string
|
||||
x-go-name: ExpirationDateTime
|
||||
failover:
|
||||
type: boolean
|
||||
x-go-name: Failover
|
||||
failovernode:
|
||||
fail_over_peers:
|
||||
additionalProperties:
|
||||
type: object
|
||||
type: object
|
||||
x-go-name: FailOverPeers
|
||||
failed_over_by:
|
||||
format: uuid
|
||||
type: string
|
||||
x-go-name: FailoverNode
|
||||
x-go-name: FailedOverBy
|
||||
hostid:
|
||||
format: uuid
|
||||
type: string
|
||||
|
@ -773,6 +1051,8 @@ definitions:
|
|||
format: uuid
|
||||
type: string
|
||||
x-go-name: ID
|
||||
inet_node_req:
|
||||
$ref: '#/definitions/InetNodeReq'
|
||||
ingressdns:
|
||||
type: string
|
||||
x-go-name: IngressDNS
|
||||
|
@ -782,14 +1062,21 @@ definitions:
|
|||
ingressgatewayrange6:
|
||||
type: string
|
||||
x-go-name: IngressGatewayRange6
|
||||
internetgateway:
|
||||
$ref: '#/definitions/UDPAddr'
|
||||
internetgw_node_id:
|
||||
type: string
|
||||
x-go-name: InternetGwID
|
||||
is_fail_over:
|
||||
type: boolean
|
||||
x-go-name: IsFailOver
|
||||
isegressgateway:
|
||||
type: boolean
|
||||
x-go-name: IsEgressGateway
|
||||
isingressgateway:
|
||||
type: boolean
|
||||
x-go-name: IsIngressGateway
|
||||
isinternetgateway:
|
||||
type: boolean
|
||||
x-go-name: IsInternetGateway
|
||||
isrelay:
|
||||
type: boolean
|
||||
x-go-name: IsRelay
|
||||
|
@ -810,6 +1097,9 @@ definitions:
|
|||
x-go-name: LastPeerUpdate
|
||||
localaddress:
|
||||
$ref: '#/definitions/IPNet'
|
||||
metadata:
|
||||
type: string
|
||||
x-go-name: Metadata
|
||||
network:
|
||||
type: string
|
||||
x-go-name: Network
|
||||
|
@ -823,8 +1113,6 @@ definitions:
|
|||
pendingdelete:
|
||||
type: boolean
|
||||
x-go-name: PendingDelete
|
||||
persistentkeepalive:
|
||||
$ref: '#/definitions/Duration'
|
||||
relayedby:
|
||||
type: string
|
||||
x-go-name: RelayedBy
|
||||
|
@ -918,6 +1206,8 @@ definitions:
|
|||
type: string
|
||||
APIPort:
|
||||
type: string
|
||||
AllowedEmailDomains:
|
||||
type: string
|
||||
AllowedOrigin:
|
||||
type: string
|
||||
AuthProvider:
|
||||
|
@ -930,6 +1220,8 @@ definitions:
|
|||
type: string
|
||||
BrokerType:
|
||||
type: string
|
||||
CacheEnabled:
|
||||
type: string
|
||||
ClientID:
|
||||
type: string
|
||||
ClientSecret:
|
||||
|
@ -965,6 +1257,8 @@ definitions:
|
|||
IsEE:
|
||||
type: string
|
||||
x-go-name: IsPro
|
||||
JwtValidityDuration:
|
||||
$ref: '#/definitions/Duration'
|
||||
LicenseValue:
|
||||
type: string
|
||||
MQPassword:
|
||||
|
@ -997,6 +1291,8 @@ definitions:
|
|||
type: string
|
||||
PublicIPService:
|
||||
type: string
|
||||
RacAutoDisable:
|
||||
type: boolean
|
||||
RestBackend:
|
||||
type: string
|
||||
SQLConn:
|
||||
|
@ -1033,6 +1329,9 @@ definitions:
|
|||
type: integer
|
||||
Version:
|
||||
type: string
|
||||
endpoint_detection:
|
||||
type: boolean
|
||||
x-go-name: EndpointDetection
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/config
|
||||
Signal:
|
||||
|
@ -1040,9 +1339,18 @@ definitions:
|
|||
properties:
|
||||
action:
|
||||
$ref: '#/definitions/SignalAction'
|
||||
from_host_id:
|
||||
type: string
|
||||
x-go-name: FromHostID
|
||||
from_host_pubkey:
|
||||
type: string
|
||||
x-go-name: FromHostPubKey
|
||||
from_node_id:
|
||||
type: string
|
||||
x-go-name: FromNodeID
|
||||
is_pro:
|
||||
type: boolean
|
||||
x-go-name: IsPro
|
||||
reply:
|
||||
type: boolean
|
||||
x-go-name: Reply
|
||||
|
@ -1053,12 +1361,15 @@ definitions:
|
|||
format: int64
|
||||
type: integer
|
||||
x-go-name: TimeStamp
|
||||
to_host_id:
|
||||
type: string
|
||||
x-go-name: ToHostID
|
||||
to_host_pubkey:
|
||||
type: string
|
||||
x-go-name: ToHostPubKey
|
||||
turn_relay_addr:
|
||||
to_node_id:
|
||||
type: string
|
||||
x-go-name: TurnRelayEndpoint
|
||||
x-go-name: ToNodeID
|
||||
type: object
|
||||
x-go-package: github.com/gravitl/netmaker/models
|
||||
SignalAction:
|
||||
|
@ -1114,6 +1425,10 @@ definitions:
|
|||
issuperadmin:
|
||||
type: boolean
|
||||
x-go-name: IsSuperAdmin
|
||||
last_login_time:
|
||||
format: date-time
|
||||
type: string
|
||||
x-go-name: LastLoginTime
|
||||
password:
|
||||
type: string
|
||||
x-go-name: Password
|
||||
|
@ -1149,7 +1464,7 @@ info:
|
|||
|
||||
API calls must be authenticated via a header of the format -H “Authorization: Bearer <YOUR_SECRET_KEY>” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes/<network>/authenticate endpoint, as documented below.
|
||||
title: Netmaker
|
||||
version: 0.23.0
|
||||
version: 0.24.0
|
||||
paths:
|
||||
/api/dns:
|
||||
get:
|
||||
|
@ -1277,6 +1592,17 @@ paths:
|
|||
summary: Push DNS entries to nameserver.
|
||||
tags:
|
||||
- dns
|
||||
/api/emqx/hosts:
|
||||
delete:
|
||||
operationId: delEmqxHosts
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/apiHostResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: Lists all hosts.
|
||||
tags:
|
||||
- hosts
|
||||
/api/extclients:
|
||||
get:
|
||||
operationId: getAllExtClients
|
||||
|
@ -1468,7 +1794,7 @@ paths:
|
|||
operationId: getHosts
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/apiHostResponse'
|
||||
$ref: '#/responses/apiHostSliceResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: Lists all hosts.
|
||||
|
@ -1728,12 +2054,6 @@ paths:
|
|||
required: true
|
||||
type: string
|
||||
x-go-name: Networkname
|
||||
- description: ACL Container
|
||||
in: body
|
||||
name: acl_container
|
||||
schema:
|
||||
$ref: '#/definitions/ACLContainer'
|
||||
x-go-name: ACLContainer
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/aclContainerResponse'
|
||||
|
@ -1765,6 +2085,41 @@ paths:
|
|||
summary: Update a network ACL (Access Control List).
|
||||
tags:
|
||||
- networks
|
||||
/api/networks/{networkname}/acls/v2:
|
||||
put:
|
||||
operationId: updateNetworkACL
|
||||
parameters:
|
||||
- description: 'name: network name'
|
||||
in: path
|
||||
name: networkname
|
||||
required: true
|
||||
type: string
|
||||
x-go-name: Networkname
|
||||
- description: ACL Container
|
||||
in: body
|
||||
name: acl_container
|
||||
schema:
|
||||
$ref: '#/definitions/ACLContainer'
|
||||
x-go-name: ACLContainer
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/aclContainerResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: Update a network ACL (Access Control List).
|
||||
tags:
|
||||
- networks
|
||||
/api/node/{nodeid}/failOverME:
|
||||
post:
|
||||
operationId: failOver_me
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/nodeResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: Create a relay.
|
||||
tags:
|
||||
- node
|
||||
/api/nodes:
|
||||
get:
|
||||
operationId: getAllNodes
|
||||
|
@ -2014,6 +2369,37 @@ paths:
|
|||
summary: Remove a relay.
|
||||
tags:
|
||||
- nodes
|
||||
/api/nodes/{network}/{nodeid}/inet_gw:
|
||||
delete:
|
||||
operationId: deleteInternetGw
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/nodeResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: Delete an internet gw.
|
||||
tags:
|
||||
- nodes
|
||||
post:
|
||||
operationId: createInternetGw
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/nodeResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: Create an inet node.
|
||||
tags:
|
||||
- nodes
|
||||
put:
|
||||
operationId: updateInternetGw
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/nodeResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: update an inet node.
|
||||
tags:
|
||||
- nodes
|
||||
/api/nodes/{network}/{nodeid}/ingress/users:
|
||||
get:
|
||||
operationId: ingressGatewayUsers
|
||||
|
@ -2287,6 +2673,49 @@ paths:
|
|||
summary: Transfers superadmin role to an admin user.
|
||||
tags:
|
||||
- user
|
||||
/api/users_pending:
|
||||
get:
|
||||
operationId: getPendingUsers
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/userBodyResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: Get all pending users.
|
||||
tags:
|
||||
- user
|
||||
/api/users_pending/{username}/pending:
|
||||
delete:
|
||||
operationId: deleteAllPendingUsers
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/userBodyResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: delete all pending users.
|
||||
tags:
|
||||
- user
|
||||
/api/users_pending/user/{username}:
|
||||
delete:
|
||||
operationId: deletePendingUser
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/userBodyResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: delete pending user.
|
||||
tags:
|
||||
- user
|
||||
post:
|
||||
operationId: approvePendingUser
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/userBodyResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: approve pending user.
|
||||
tags:
|
||||
- user
|
||||
/api/v1/enrollment-keys:
|
||||
get:
|
||||
operationId: getEnrollmentKeys
|
||||
|
@ -2300,6 +2729,13 @@ paths:
|
|||
- enrollmentKeys
|
||||
post:
|
||||
operationId: createEnrollmentKey
|
||||
parameters:
|
||||
- description: APIEnrollmentKey
|
||||
in: body
|
||||
name: body
|
||||
schema:
|
||||
$ref: '#/definitions/APIEnrollmentKey'
|
||||
x-go-name: Body
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/EnrollmentKey'
|
||||
|
@ -2325,6 +2761,29 @@ paths:
|
|||
summary: Deletes an EnrollmentKey from Netmaker server.
|
||||
tags:
|
||||
- enrollmentKeys
|
||||
put:
|
||||
operationId: updateEnrollmentKey
|
||||
parameters:
|
||||
- description: KeyID
|
||||
in: path
|
||||
name: keyid
|
||||
required: true
|
||||
type: string
|
||||
x-go-name: KeyID
|
||||
- description: APIEnrollmentKey
|
||||
in: body
|
||||
name: body
|
||||
schema:
|
||||
$ref: '#/definitions/APIEnrollmentKey'
|
||||
x-go-name: Body
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/EnrollmentKey'
|
||||
schemes:
|
||||
- https
|
||||
summary: Updates an EnrollmentKey for hosts to use on Netmaker server. Updates only the relay to use.
|
||||
tags:
|
||||
- enrollmentKeys
|
||||
/api/v1/enrollment-keys/{token}:
|
||||
post:
|
||||
operationId: handleHostRegister
|
||||
|
@ -2347,6 +2806,17 @@ paths:
|
|||
summary: Handles a Netclient registration with server and add nodes accordingly.
|
||||
tags:
|
||||
- enrollmentKeys
|
||||
/api/v1/fallback/host/{hostid}:
|
||||
put:
|
||||
operationId: hostUpdateFallback
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/apiHostResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: Updates a Netclient host on Netmaker server.
|
||||
tags:
|
||||
- hosts
|
||||
/api/v1/host:
|
||||
get:
|
||||
description: Used by clients for "pull" command
|
||||
|
@ -2369,6 +2839,27 @@ paths:
|
|||
summary: Delete all legacy nodes from DB.
|
||||
tags:
|
||||
- nodes
|
||||
/api/v1/node/failover:
|
||||
delete:
|
||||
operationId: deletefailOver
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/nodeResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: Create a relay.
|
||||
tags:
|
||||
- node
|
||||
post:
|
||||
operationId: createfailOver
|
||||
responses:
|
||||
"200":
|
||||
$ref: '#/responses/nodeResponse'
|
||||
schemes:
|
||||
- https
|
||||
summary: Create a relay.
|
||||
tags:
|
||||
- node
|
||||
/api/v1/nodes/migrate:
|
||||
put:
|
||||
operationId: migrateData
|
||||
|
@ -2423,6 +2914,12 @@ responses:
|
|||
description: ""
|
||||
schema:
|
||||
$ref: '#/definitions/ApiHost'
|
||||
apiHostSliceResponse:
|
||||
description: ""
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/ApiHost'
|
||||
type: array
|
||||
byteArrayResponse:
|
||||
description: ""
|
||||
schema:
|
||||
|
@ -2474,7 +2971,7 @@ responses:
|
|||
description: ""
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/LegacyNode'
|
||||
$ref: '#/definitions/ApiNode'
|
||||
type: array
|
||||
okResponse:
|
||||
description: ""
|
||||
|
|
Loading…
Reference in a new issue