mirror of
https://github.com/gravitl/netmaker.git
synced 2025-10-06 03:46:02 +08:00
Merge branch 'develop' into task/schema-changes
# Conflicts: # logic/hosts.go
This commit is contained in:
commit
a58a512f8b
39 changed files with 568 additions and 236 deletions
|
@ -77,7 +77,7 @@ func SessionHandler(conn *websocket.Conn) {
|
|||
_, err := logic.VerifyAuthRequest(models.UserAuthParams{
|
||||
UserName: registerMessage.User,
|
||||
Password: registerMessage.Password,
|
||||
})
|
||||
}, logic.NetclientApp)
|
||||
if err != nil {
|
||||
err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
if err != nil {
|
||||
|
|
|
@ -89,6 +89,7 @@ type ServerConfig struct {
|
|||
DeployedByOperator bool `yaml:"deployed_by_operator"`
|
||||
Environment string `yaml:"environment"`
|
||||
JwtValidityDuration time.Duration `yaml:"jwt_validity_duration" swaggertype:"primitive,integer" format:"int64"`
|
||||
JwtValidityDurationClients time.Duration `yaml:"jwt_validity_duration_clients" swaggertype:"primitive,integer" format:"int64"`
|
||||
RacRestrictToSingleNetwork bool `yaml:"rac_restrict_to_single_network"`
|
||||
CacheEnabled string `yaml:"caching_enabled"`
|
||||
EndpointDetection bool `yaml:"endpoint_detection"`
|
||||
|
|
|
@ -55,6 +55,7 @@ func HandleRESTRequests(wg *sync.WaitGroup, ctx context.Context) {
|
|||
"Content-Type",
|
||||
"authorization",
|
||||
"From-Ui",
|
||||
"X-Application-Name",
|
||||
},
|
||||
)
|
||||
originsOk := handlers.AllowedOrigins(strings.Split(servercfg.GetAllowedOrigin(), ","))
|
||||
|
|
|
@ -3,6 +3,7 @@ package controller
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
@ -56,6 +57,7 @@ func serverHandlers(r *mux.Router) {
|
|||
Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/server/mem_profile", logic.SecurityCheck(false, http.HandlerFunc(memProfile))).
|
||||
Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/server/feature_flags", getFeatureFlags).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
func cpuProfile(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -274,11 +276,11 @@ func updateSettings(w http.ResponseWriter, r *http.Request) {
|
|||
currSettings := logic.GetServerSettings()
|
||||
err := logic.UpsertServerSettings(req)
|
||||
if err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to udpate server settings "+err.Error()), "internal"))
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to update server settings "+err.Error()), "internal"))
|
||||
return
|
||||
}
|
||||
logic.LogEvent(&models.Event{
|
||||
Action: models.Update,
|
||||
Action: identifySettingsUpdateAction(currSettings, req),
|
||||
Source: models.Subject{
|
||||
ID: r.Header.Get("user"),
|
||||
Name: r.Header.Get("user"),
|
||||
|
@ -323,5 +325,97 @@ func reInit(curr, new models.ServerSettings, force bool) {
|
|||
}
|
||||
}
|
||||
go mq.PublishPeerUpdate(false)
|
||||
|
||||
}
|
||||
|
||||
func identifySettingsUpdateAction(old, new models.ServerSettings) models.Action {
|
||||
// TODO: here we are relying on the dashboard to only
|
||||
// make singular updates, but it's possible that the
|
||||
// API can be called to make multiple changes to the
|
||||
// server settings. We should update it to log multiple
|
||||
// events or create singular update APIs.
|
||||
if old.MFAEnforced != new.MFAEnforced {
|
||||
if new.MFAEnforced {
|
||||
return models.EnforceMFA
|
||||
} else {
|
||||
return models.UnenforceMFA
|
||||
}
|
||||
}
|
||||
|
||||
if old.BasicAuth != new.BasicAuth {
|
||||
if new.BasicAuth {
|
||||
return models.EnableBasicAuth
|
||||
} else {
|
||||
return models.DisableBasicAuth
|
||||
}
|
||||
}
|
||||
|
||||
if old.Telemetry != new.Telemetry {
|
||||
if new.Telemetry == "off" {
|
||||
return models.DisableTelemetry
|
||||
} else {
|
||||
return models.EnableTelemetry
|
||||
}
|
||||
}
|
||||
|
||||
if old.NetclientAutoUpdate != new.NetclientAutoUpdate ||
|
||||
old.RacRestrictToSingleNetwork != new.RacRestrictToSingleNetwork ||
|
||||
old.ManageDNS != new.ManageDNS ||
|
||||
old.DefaultDomain != new.DefaultDomain ||
|
||||
old.EndpointDetection != new.EndpointDetection {
|
||||
return models.UpdateClientSettings
|
||||
}
|
||||
|
||||
if old.AllowedEmailDomains != new.AllowedEmailDomains ||
|
||||
old.JwtValidityDuration != new.JwtValidityDuration {
|
||||
return models.UpdateAuthenticationSecuritySettings
|
||||
}
|
||||
|
||||
if old.Verbosity != new.Verbosity ||
|
||||
old.MetricsPort != new.MetricsPort ||
|
||||
old.MetricInterval != new.MetricInterval ||
|
||||
old.AuditLogsRetentionPeriodInDays != new.AuditLogsRetentionPeriodInDays {
|
||||
return models.UpdateMonitoringAndDebuggingSettings
|
||||
}
|
||||
|
||||
if old.Theme != new.Theme {
|
||||
return models.UpdateDisplaySettings
|
||||
}
|
||||
|
||||
if old.TextSize != new.TextSize ||
|
||||
old.ReducedMotion != new.ReducedMotion {
|
||||
return models.UpdateAccessibilitySettings
|
||||
}
|
||||
|
||||
if old.EmailSenderAddr != new.EmailSenderAddr ||
|
||||
old.EmailSenderUser != new.EmailSenderUser ||
|
||||
old.EmailSenderPassword != new.EmailSenderPassword ||
|
||||
old.SmtpHost != new.SmtpHost ||
|
||||
old.SmtpPort != new.SmtpPort {
|
||||
return models.UpdateSMTPSettings
|
||||
}
|
||||
|
||||
if old.AuthProvider != new.AuthProvider ||
|
||||
old.OIDCIssuer != new.OIDCIssuer ||
|
||||
old.ClientID != new.ClientID ||
|
||||
old.ClientSecret != new.ClientSecret ||
|
||||
old.SyncEnabled != new.SyncEnabled ||
|
||||
old.IDPSyncInterval != new.IDPSyncInterval ||
|
||||
old.GoogleAdminEmail != new.GoogleAdminEmail ||
|
||||
old.GoogleSACredsJson != new.GoogleSACredsJson ||
|
||||
old.AzureTenant != new.AzureTenant ||
|
||||
!cmp.Equal(old.GroupFilters, new.GroupFilters) ||
|
||||
cmp.Equal(old.UserFilters, new.UserFilters) {
|
||||
return models.UpdateIDPSettings
|
||||
}
|
||||
|
||||
return models.Update
|
||||
}
|
||||
|
||||
// @Summary Get feature flags for this server.
|
||||
// @Router /api/server/feature_flags [get]
|
||||
// @Tags Server
|
||||
// @Security oauth2
|
||||
// @Success 200 {object} config.ServerSettings
|
||||
func getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||
logic.ReturnSuccessResponseWithJson(w, r, logic.GetFeatureFlags(), "")
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"github.com/pquerna/otp"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"image/png"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
@ -38,6 +39,7 @@ func userHandlers(r *mux.Router) {
|
|||
r.HandleFunc("/api/users/adm/transfersuperadmin/{username}", logic.SecurityCheck(true, http.HandlerFunc(transferSuperAdmin))).
|
||||
Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/users/{username}/validate-identity", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(validateUserIdentity)))).Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/users/{username}/auth/init-totp", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(initiateTOTPSetup)))).Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/users/{username}/auth/complete-totp", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(completeTOTPSetup)))).Methods(http.MethodPost)
|
||||
r.HandleFunc("/api/users/{username}/auth/verify-totp", logic.PreAuthCheck(logic.ContinueIfUserMatch(http.HandlerFunc(verifyTOTP)))).Methods(http.MethodPost)
|
||||
|
@ -253,6 +255,10 @@ func deleteUserAccessTokens(w http.ResponseWriter, r *http.Request) {
|
|||
// @Failure 401 {object} models.ErrorResponse
|
||||
// @Failure 500 {object} models.ErrorResponse
|
||||
func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
||||
appName := request.Header.Get("X-Application-Name")
|
||||
if appName == "" {
|
||||
appName = logic.NetmakerDesktopApp
|
||||
}
|
||||
|
||||
// Auth request consists of Mac Address and Password (from node that is authorizing
|
||||
// in case of Master, auth is ignored and mac is set to "mastermac"
|
||||
|
@ -308,42 +314,10 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|||
logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("access denied to dashboard"), "unauthorized"))
|
||||
return
|
||||
}
|
||||
// log user activity
|
||||
logic.LogEvent(&models.Event{
|
||||
Action: models.Login,
|
||||
Source: models.Subject{
|
||||
ID: user.UserName,
|
||||
Name: user.UserName,
|
||||
Type: models.UserSub,
|
||||
},
|
||||
TriggeredBy: user.UserName,
|
||||
Target: models.Subject{
|
||||
ID: models.DashboardSub.String(),
|
||||
Name: models.DashboardSub.String(),
|
||||
Type: models.DashboardSub,
|
||||
},
|
||||
Origin: models.Dashboard,
|
||||
})
|
||||
} else {
|
||||
logic.LogEvent(&models.Event{
|
||||
Action: models.Login,
|
||||
Source: models.Subject{
|
||||
ID: user.UserName,
|
||||
Name: user.UserName,
|
||||
Type: models.UserSub,
|
||||
},
|
||||
TriggeredBy: user.UserName,
|
||||
Target: models.Subject{
|
||||
ID: models.ClientAppSub.String(),
|
||||
Name: models.ClientAppSub.String(),
|
||||
Type: models.ClientAppSub,
|
||||
},
|
||||
Origin: models.ClientApp,
|
||||
})
|
||||
}
|
||||
|
||||
username := authRequest.UserName
|
||||
jwt, err := logic.VerifyAuthRequest(authRequest)
|
||||
jwt, err := logic.VerifyAuthRequest(authRequest, appName)
|
||||
if err != nil {
|
||||
logger.Log(0, username, "user validation failed: ",
|
||||
err.Error())
|
||||
|
@ -393,6 +367,44 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|||
return
|
||||
}
|
||||
logger.Log(2, username, "was authenticated")
|
||||
|
||||
// log user activity
|
||||
if !user.IsMFAEnabled {
|
||||
if val := request.Header.Get("From-Ui"); val == "true" {
|
||||
logic.LogEvent(&models.Event{
|
||||
Action: models.Login,
|
||||
Source: models.Subject{
|
||||
ID: user.UserName,
|
||||
Name: user.UserName,
|
||||
Type: models.UserSub,
|
||||
},
|
||||
TriggeredBy: user.UserName,
|
||||
Target: models.Subject{
|
||||
ID: models.DashboardSub.String(),
|
||||
Name: models.DashboardSub.String(),
|
||||
Type: models.DashboardSub,
|
||||
},
|
||||
Origin: models.Dashboard,
|
||||
})
|
||||
} else {
|
||||
logic.LogEvent(&models.Event{
|
||||
Action: models.Login,
|
||||
Source: models.Subject{
|
||||
ID: user.UserName,
|
||||
Name: user.UserName,
|
||||
Type: models.UserSub,
|
||||
},
|
||||
TriggeredBy: user.UserName,
|
||||
Target: models.Subject{
|
||||
ID: models.ClientAppSub.String(),
|
||||
Name: models.ClientAppSub.String(),
|
||||
Type: models.ClientAppSub,
|
||||
},
|
||||
Origin: models.ClientApp,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
response.Write(successJSONResponse)
|
||||
|
||||
|
@ -434,6 +446,43 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|||
}()
|
||||
}
|
||||
|
||||
// @Summary Validates a user's identity against it's token. This is used by UI before a user performing a critical operation to validate the user's identity.
|
||||
// @Router /api/users/{username}/validate-identity [post]
|
||||
// @Tags Auth
|
||||
// @Accept json
|
||||
// @Param body body models.UserIdentityValidationRequest true "User Identity Validation Request"
|
||||
// @Success 200 {object} models.SuccessResponse
|
||||
// @Failure 400 {object} models.ErrorResponse
|
||||
func validateUserIdentity(w http.ResponseWriter, r *http.Request) {
|
||||
username := r.Header.Get("user")
|
||||
|
||||
var req models.UserIdentityValidationRequest
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to decode request body: ", err.Error())
|
||||
err = fmt.Errorf("invalid request body: %v", err)
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := logic.GetUser(username)
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to get user: ", err.Error())
|
||||
err = fmt.Errorf("user not found: %v", err)
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
|
||||
var resp models.UserIdentityValidationResponse
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
|
||||
if err != nil {
|
||||
logic.ReturnSuccessResponseWithJson(w, r, resp, "user identity validation failed")
|
||||
} else {
|
||||
resp.IdentityValidated = true
|
||||
logic.ReturnSuccessResponseWithJson(w, r, resp, "user identity validated")
|
||||
}
|
||||
}
|
||||
|
||||
// @Summary Initiate setting up TOTP 2FA for a user.
|
||||
// @Router /api/users/auth/init-totp [post]
|
||||
// @Tags Auth
|
||||
|
@ -557,6 +606,22 @@ func completeTOTPSetup(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
logic.LogEvent(&models.Event{
|
||||
Action: models.EnableMFA,
|
||||
Source: models.Subject{
|
||||
ID: user.UserName,
|
||||
Name: user.UserName,
|
||||
Type: models.UserSub,
|
||||
},
|
||||
TriggeredBy: user.UserName,
|
||||
Target: models.Subject{
|
||||
ID: user.UserName,
|
||||
Name: user.UserName,
|
||||
Type: models.UserSub,
|
||||
},
|
||||
Origin: models.Dashboard,
|
||||
})
|
||||
|
||||
logic.ReturnSuccessResponse(w, r, fmt.Sprintf("totp setup complete for user %s", username))
|
||||
} else {
|
||||
err = fmt.Errorf("cannot setup totp for user %s: invalid otp", username)
|
||||
|
@ -576,6 +641,11 @@ func completeTOTPSetup(w http.ResponseWriter, r *http.Request) {
|
|||
func verifyTOTP(w http.ResponseWriter, r *http.Request) {
|
||||
username := r.Header.Get("user")
|
||||
|
||||
appName := r.Header.Get("X-Application-Name")
|
||||
if appName == "" {
|
||||
appName = logic.NetmakerDesktopApp
|
||||
}
|
||||
|
||||
var req models.UserTOTPVerificationParams
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
|
@ -601,7 +671,7 @@ func verifyTOTP(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if totp.Validate(req.TOTP, user.TOTPSecret) {
|
||||
jwt, err := logic.CreateUserJWT(user.UserName, user.PlatformRoleID)
|
||||
jwt, err := logic.CreateUserJWT(user.UserName, user.PlatformRoleID, appName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error creating token: %v", err)
|
||||
logger.Log(0, err.Error())
|
||||
|
@ -619,6 +689,22 @@ func verifyTOTP(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
logic.LogEvent(&models.Event{
|
||||
Action: models.Login,
|
||||
Source: models.Subject{
|
||||
ID: user.UserName,
|
||||
Name: user.UserName,
|
||||
Type: models.UserSub,
|
||||
},
|
||||
TriggeredBy: user.UserName,
|
||||
Target: models.Subject{
|
||||
ID: models.DashboardSub.String(),
|
||||
Name: models.DashboardSub.String(),
|
||||
Type: models.DashboardSub,
|
||||
},
|
||||
Origin: models.Dashboard,
|
||||
})
|
||||
|
||||
logic.ReturnSuccessResponseWithJson(w, r, models.SuccessfulUserLoginResponse{
|
||||
UserName: username,
|
||||
AuthToken: jwt,
|
||||
|
@ -1135,8 +1221,22 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
|||
UserName: logic.MasterUser,
|
||||
}
|
||||
}
|
||||
action := models.Update
|
||||
// TODO: here we are relying on the dashboard to only
|
||||
// make singular updates, but it's possible that the
|
||||
// API can be called to make multiple changes to the
|
||||
// user. We should update it to log multiple events
|
||||
// or create singular update APIs.
|
||||
if userchange.IsMFAEnabled != user.IsMFAEnabled {
|
||||
if userchange.IsMFAEnabled {
|
||||
// the update API won't be used to enable MFA.
|
||||
action = models.EnableMFA
|
||||
} else {
|
||||
action = models.DisableMFA
|
||||
}
|
||||
}
|
||||
e := models.Event{
|
||||
Action: models.Update,
|
||||
Action: action,
|
||||
Source: models.Subject{
|
||||
ID: caller.UserName,
|
||||
Name: caller.UserName,
|
||||
|
@ -1233,6 +1333,14 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
if user.AuthType == models.OAuth || user.ExternalIdentityProviderID != "" {
|
||||
err = fmt.Errorf("cannot delete idp user %s", username)
|
||||
logger.Log(0, username, "failed to delete user: ", err.Error())
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
|
||||
err = logic.DeleteUser(username)
|
||||
if err != nil {
|
||||
logger.Log(0, username,
|
||||
|
|
6
go.mod
6
go.mod
|
@ -7,7 +7,7 @@ toolchain go1.23.7
|
|||
require (
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.0
|
||||
github.com/go-playground/validator/v10 v10.26.0
|
||||
github.com/go-playground/validator/v10 v10.27.0
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/handlers v1.5.2
|
||||
|
@ -49,9 +49,9 @@ require (
|
|||
github.com/matryer/is v1.4.1
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
google.golang.org/api v0.238.0
|
||||
google.golang.org/api v0.240.0
|
||||
gopkg.in/mail.v2 v2.3.1
|
||||
gorm.io/datatypes v1.2.5
|
||||
gorm.io/datatypes v1.2.6
|
||||
gorm.io/driver/postgres v1.6.0
|
||||
gorm.io/driver/sqlite v1.6.0
|
||||
gorm.io/gorm v1.30.0
|
||||
|
|
16
go.sum
16
go.sum
|
@ -39,8 +39,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
|||
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.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
||||
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
|
@ -176,8 +176,8 @@ golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
|||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb h1:9aqVcYEDHmSNb0uOWukxV5lHV09WqiSiCuhEgWNETLY=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ=
|
||||
google.golang.org/api v0.238.0 h1:+EldkglWIg/pWjkq97sd+XxH7PxakNYoe/rkSTbnvOs=
|
||||
google.golang.org/api v0.238.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
|
||||
google.golang.org/api v0.240.0 h1:PxG3AA2UIqT1ofIzWV2COM3j3JagKTKSwy7L6RHNXNU=
|
||||
google.golang.org/api v0.240.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
|
||||
|
@ -198,16 +198,16 @@ gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
|
|||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/datatypes v1.2.5 h1:9UogU3jkydFVW1bIVVeoYsTpLRgwDVW3rHfJG6/Ek9I=
|
||||
gorm.io/datatypes v1.2.5/go.mod h1:I5FUdlKpLb5PMqeMQhm30CQ6jXP8Rj89xkTeCSAaAD4=
|
||||
gorm.io/datatypes v1.2.6 h1:KafLdXvFUhzNeL2ncm03Gl3eTLONQfNKZ+wJ+9Y4Nck=
|
||||
gorm.io/datatypes v1.2.6/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
|
||||
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
|
||||
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
||||
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
||||
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||
gorm.io/driver/sqlserver v1.5.4 h1:xA+Y1KDNspv79q43bPyjDMUgHoYHLhXYmdFcYPobg8g=
|
||||
gorm.io/driver/sqlserver v1.5.4/go.mod h1:+frZ/qYmuna11zHPlh5oc2O6ZA/lS88Keb0XSH1Zh/g=
|
||||
gorm.io/driver/sqlserver v1.6.0 h1:VZOBQVsVhkHU/NzNhRJKoANt5pZGQAS1Bwc6m6dgfnc=
|
||||
gorm.io/driver/sqlserver v1.6.0/go.mod h1:WQzt4IJo/WHKnckU9jXBLMJIVNMVeTu25dnOzehntWw=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
|
||||
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||
|
|
|
@ -655,12 +655,12 @@ var IsAclPolicyValid = func(acl models.Acl) (err error) {
|
|||
|
||||
var IsPeerAllowed = func(node, peer models.Node, checkDefaultPolicy bool) bool {
|
||||
var nodeId, peerId string
|
||||
if node.IsGw && peer.IsRelayed && peer.RelayedBy == node.ID.String() {
|
||||
return true
|
||||
}
|
||||
if peer.IsGw && node.IsRelayed && node.RelayedBy == peer.ID.String() {
|
||||
return true
|
||||
}
|
||||
// if node.IsGw && peer.IsRelayed && peer.RelayedBy == node.ID.String() {
|
||||
// return true
|
||||
// }
|
||||
// if peer.IsGw && node.IsRelayed && node.RelayedBy == peer.ID.String() {
|
||||
// return true
|
||||
// }
|
||||
if node.IsStatic {
|
||||
nodeId = node.StaticNode.ClientID
|
||||
node = node.StaticNode.ConvertToStaticNode()
|
||||
|
|
|
@ -24,6 +24,12 @@ const (
|
|||
auth_key = "netmaker_auth"
|
||||
)
|
||||
|
||||
const (
|
||||
DashboardApp = "dashboard"
|
||||
NetclientApp = "netclient"
|
||||
NetmakerDesktopApp = "netmaker-desktop"
|
||||
)
|
||||
|
||||
var (
|
||||
superUser = models.User{}
|
||||
)
|
||||
|
@ -178,7 +184,8 @@ func CreateUser(user *models.User) error {
|
|||
user.AuthType = models.OAuth
|
||||
}
|
||||
AddGlobalNetRolesToAdmins(user)
|
||||
_, err = CreateUserJWT(user.UserName, user.PlatformRoleID)
|
||||
// create user will always be called either from API or Dashboard.
|
||||
_, err = CreateUserJWT(user.UserName, user.PlatformRoleID, DashboardApp)
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to generate token", err.Error())
|
||||
return err
|
||||
|
@ -212,7 +219,7 @@ func CreateSuperAdmin(u *models.User) error {
|
|||
}
|
||||
|
||||
// VerifyAuthRequest - verifies an auth request
|
||||
func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
|
||||
func VerifyAuthRequest(authRequest models.UserAuthParams, appName string) (string, error) {
|
||||
var result models.User
|
||||
if authRequest.UserName == "" {
|
||||
return "", errors.New("username can't be empty")
|
||||
|
@ -245,7 +252,7 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
|
|||
return tokenString, nil
|
||||
} else {
|
||||
// Create a new JWT for the node
|
||||
tokenString, err := CreateUserJWT(authRequest.UserName, result.PlatformRoleID)
|
||||
tokenString, err := CreateUserJWT(authRequest.UserName, result.PlatformRoleID, appName)
|
||||
if err != nil {
|
||||
slog.Error("error creating jwt", "error", err)
|
||||
return "", err
|
||||
|
@ -483,8 +490,9 @@ func GetState(state string) (*models.SsoState, error) {
|
|||
}
|
||||
|
||||
// SetState - sets a state with new expiration
|
||||
func SetState(state string) error {
|
||||
func SetState(appName, state string) error {
|
||||
s := models.SsoState{
|
||||
AppName: appName,
|
||||
Value: state,
|
||||
Expiration: time.Now().Add(models.DefaultExpDuration),
|
||||
}
|
||||
|
|
|
@ -465,7 +465,7 @@ func GetAllExtClientsWithStatus(status models.NodeStatus) ([]models.ExtClient, e
|
|||
var validExtClients []models.ExtClient
|
||||
for _, extClient := range extClients {
|
||||
node := extClient.ConvertToStaticNode()
|
||||
GetNodeCheckInStatus(&node, false)
|
||||
GetNodeStatus(&node, false)
|
||||
|
||||
if node.Status == status {
|
||||
validExtClients = append(validExtClients, extClient)
|
||||
|
|
|
@ -4,6 +4,9 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gravitl/netmaker/converters"
|
||||
"github.com/gravitl/netmaker/database"
|
||||
|
@ -15,7 +18,6 @@ import (
|
|||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/exp/slog"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -219,6 +221,10 @@ func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool)
|
|||
sendPeerUpdate = true
|
||||
isEndpointChanged = true
|
||||
}
|
||||
if !reflect.DeepEqual(currHost.Interfaces, newHost.Interfaces) {
|
||||
currHost.Interfaces = newHost.Interfaces
|
||||
sendPeerUpdate = true
|
||||
}
|
||||
|
||||
if isEndpointChanged {
|
||||
for _, nodeID := range currHost.Nodes {
|
||||
|
|
|
@ -83,9 +83,13 @@ func CreateUserAccessJwtToken(username string, role models.UserRoleID, d time.Ti
|
|||
}
|
||||
|
||||
// CreateUserJWT - creates a user jwt token
|
||||
func CreateUserJWT(username string, role models.UserRoleID) (response string, err error) {
|
||||
settings := GetServerSettings()
|
||||
expirationTime := time.Now().Add(time.Duration(settings.JwtValidityDuration) * time.Minute)
|
||||
func CreateUserJWT(username string, role models.UserRoleID, appName string) (response string, err error) {
|
||||
duration := GetJwtValidityDuration()
|
||||
if appName == NetclientApp || appName == NetmakerDesktopApp {
|
||||
duration = GetJwtValidityDurationForClients()
|
||||
}
|
||||
|
||||
expirationTime := time.Now().Add(duration)
|
||||
claims := &models.UserClaims{
|
||||
UserName: username,
|
||||
Role: role,
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package logic
|
||||
|
||||
import "github.com/gravitl/netmaker/models"
|
||||
|
||||
// EnterpriseCheckFuncs - can be set to run functions for EE
|
||||
var EnterpriseCheckFuncs []func()
|
||||
var GetFeatureFlags = func() models.FeatureFlags {
|
||||
return models.FeatureFlags{}
|
||||
}
|
||||
|
||||
// == Join, Checkin, and Leave for Server ==
|
||||
|
||||
|
|
|
@ -33,6 +33,11 @@ func UpsertServerSettings(s models.ServerSettings) error {
|
|||
if s.ClientSecret == Mask() {
|
||||
s.ClientSecret = currSettings.ClientSecret
|
||||
}
|
||||
|
||||
if servercfg.DeployedByOperator() {
|
||||
s.BasicAuth = true
|
||||
}
|
||||
|
||||
data, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -46,22 +51,28 @@ func UpsertServerSettings(s models.ServerSettings) error {
|
|||
|
||||
func ValidateNewSettings(req models.ServerSettings) bool {
|
||||
// TODO: add checks for different fields
|
||||
if req.JwtValidityDuration > 525600 || req.JwtValidityDuration < 5 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func GetServerSettingsFromEnv() (s models.ServerSettings) {
|
||||
|
||||
s = models.ServerSettings{
|
||||
NetclientAutoUpdate: servercfg.AutoUpdateEnabled(),
|
||||
Verbosity: servercfg.GetVerbosity(),
|
||||
AuthProvider: os.Getenv("AUTH_PROVIDER"),
|
||||
OIDCIssuer: os.Getenv("OIDC_ISSUER"),
|
||||
ClientID: os.Getenv("CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("CLIENT_SECRET"),
|
||||
AzureTenant: servercfg.GetAzureTenant(),
|
||||
Telemetry: servercfg.Telemetry(),
|
||||
BasicAuth: servercfg.IsBasicAuthEnabled(),
|
||||
JwtValidityDuration: servercfg.GetJwtValidityDurationFromEnv() / 60,
|
||||
NetclientAutoUpdate: servercfg.AutoUpdateEnabled(),
|
||||
Verbosity: servercfg.GetVerbosity(),
|
||||
AuthProvider: os.Getenv("AUTH_PROVIDER"),
|
||||
OIDCIssuer: os.Getenv("OIDC_ISSUER"),
|
||||
ClientID: os.Getenv("CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("CLIENT_SECRET"),
|
||||
AzureTenant: servercfg.GetAzureTenant(),
|
||||
Telemetry: servercfg.Telemetry(),
|
||||
BasicAuth: servercfg.IsBasicAuthEnabled(),
|
||||
JwtValidityDuration: servercfg.GetJwtValidityDurationFromEnv() / 60,
|
||||
// setting client's jwt validity duration to be the same as that of
|
||||
// dashboard.
|
||||
JwtValidityDurationClients: servercfg.GetJwtValidityDurationFromEnv() / 60,
|
||||
RacRestrictToSingleNetwork: servercfg.GetRacRestrictToSingleNetwork(),
|
||||
EndpointDetection: servercfg.IsEndpointDetectionEnabled(),
|
||||
AllowedEmailDomains: servercfg.GetAllowedEmailDomains(),
|
||||
|
@ -139,6 +150,7 @@ func GetServerConfig() config.ServerConfig {
|
|||
cfg.IsPro = "yes"
|
||||
}
|
||||
cfg.JwtValidityDuration = time.Duration(settings.JwtValidityDuration) * time.Minute
|
||||
cfg.JwtValidityDurationClients = time.Duration(settings.JwtValidityDurationClients) * time.Minute
|
||||
cfg.RacRestrictToSingleNetwork = settings.RacRestrictToSingleNetwork
|
||||
cfg.MetricInterval = settings.MetricInterval
|
||||
cfg.ManageDNS = settings.ManageDNS
|
||||
|
@ -201,7 +213,13 @@ func Telemetry() string {
|
|||
|
||||
// GetJwtValidityDuration - returns the JWT validity duration in minutes
|
||||
func GetJwtValidityDuration() time.Duration {
|
||||
return GetServerConfig().JwtValidityDuration
|
||||
return time.Duration(GetServerSettings().JwtValidityDuration) * time.Minute
|
||||
}
|
||||
|
||||
// GetJwtValidityDurationForClients returns the JWT validity duration in
|
||||
// minutes for clients.
|
||||
func GetJwtValidityDurationForClients() time.Duration {
|
||||
return time.Duration(GetServerSettings().JwtValidityDurationClients) * time.Minute
|
||||
}
|
||||
|
||||
// GetRacRestrictToSingleNetwork - returns whether the feature to allow simultaneous network connections via RAC is enabled
|
||||
|
@ -317,6 +335,10 @@ func GetManageDNS() bool {
|
|||
|
||||
// IsBasicAuthEnabled - checks if basic auth has been configured to be turned off
|
||||
func IsBasicAuthEnabled() bool {
|
||||
if servercfg.DeployedByOperator() {
|
||||
return true
|
||||
}
|
||||
|
||||
return GetServerSettings().BasicAuth
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/blang/semver"
|
||||
"github.com/c-robinson/iplib"
|
||||
"github.com/gravitl/netmaker/logger"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
)
|
||||
|
||||
// IsBase64 - checks if a string is in base64 format
|
||||
|
@ -242,3 +243,22 @@ func GetClientIP(r *http.Request) string {
|
|||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
// CompareIfaceSlices compares two slices of Iface for deep equality (order-sensitive)
|
||||
func CompareIfaceSlices(a, b []models.Iface) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if !compareIface(a[i], b[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
func compareIface(a, b models.Iface) bool {
|
||||
return a.Name == b.Name &&
|
||||
a.Address.IP.Equal(b.Address.IP) &&
|
||||
a.Address.Mask.String() == b.Address.Mask.String() &&
|
||||
a.AddressString == b.AddressString
|
||||
}
|
||||
|
|
|
@ -139,7 +139,10 @@ func ManageZombies(ctx context.Context, peerUpdate chan *models.Node) {
|
|||
if servercfg.IsAutoCleanUpEnabled() {
|
||||
nodes, _ := GetAllNodes()
|
||||
for _, node := range nodes {
|
||||
if time.Since(node.LastCheckIn) > time.Minute*ZOMBIE_DELETE_TIME {
|
||||
if !node.Connected {
|
||||
continue
|
||||
}
|
||||
if time.Since(node.LastCheckIn) > time.Hour*2 {
|
||||
if err := DeleteNode(&node, true); err != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -168,8 +171,8 @@ func checkPendingRemovalNodes(peerUpdate chan *models.Node) {
|
|||
peerUpdate <- &node
|
||||
continue
|
||||
}
|
||||
if servercfg.IsAutoCleanUpEnabled() {
|
||||
if time.Since(node.LastCheckIn) > time.Minute*ZOMBIE_DELETE_TIME {
|
||||
if servercfg.IsAutoCleanUpEnabled() && node.Connected {
|
||||
if time.Since(node.LastCheckIn) > time.Hour*2 {
|
||||
if err := DeleteNode(&node, true); err != nil {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -537,21 +537,27 @@ func migrateToEgressV1() {
|
|||
}
|
||||
for _, node := range nodes {
|
||||
if node.IsEgressGateway {
|
||||
egressHost, err := logic.GetHost(node.HostID.String())
|
||||
_, err := logic.GetHost(node.HostID.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, rangeI := range node.EgressGatewayRequest.Ranges {
|
||||
e := schema.Egress{
|
||||
for _, rangeMetric := range node.EgressGatewayRequest.RangesWithMetric {
|
||||
e := &schema.Egress{Range: rangeMetric.Network}
|
||||
if err := e.DoesEgressRouteExists(db.WithContext(context.TODO())); err == nil {
|
||||
e.Nodes[node.ID.String()] = rangeMetric.RouteMetric
|
||||
e.Update(db.WithContext(context.TODO()))
|
||||
continue
|
||||
}
|
||||
e = &schema.Egress{
|
||||
ID: uuid.New().String(),
|
||||
Name: fmt.Sprintf("%s egress", egressHost.Name),
|
||||
Name: fmt.Sprintf("%s egress", rangeMetric.Network),
|
||||
Description: "",
|
||||
Network: node.Network,
|
||||
Nodes: datatypes.JSONMap{
|
||||
node.ID.String(): 256,
|
||||
node.ID.String(): rangeMetric.RouteMetric,
|
||||
},
|
||||
Tags: make(datatypes.JSONMap),
|
||||
Range: rangeI,
|
||||
Range: rangeMetric.Network,
|
||||
Nat: node.EgressGatewayRequest.NatEnabled == "yes",
|
||||
Status: true,
|
||||
CreatedBy: user.UserName,
|
||||
|
@ -642,5 +648,8 @@ func settings() {
|
|||
if settings.DefaultDomain == "" {
|
||||
settings.DefaultDomain = servercfg.GetDefaultDomain()
|
||||
}
|
||||
if settings.JwtValidityDurationClients == 0 {
|
||||
settings.JwtValidityDurationClients = servercfg.GetJwtValidityDurationFromEnv() / 60
|
||||
}
|
||||
logic.UpsertServerSettings(settings)
|
||||
}
|
||||
|
|
|
@ -3,21 +3,36 @@ package models
|
|||
type Action string
|
||||
|
||||
const (
|
||||
Create Action = "CREATE"
|
||||
Update Action = "UPDATE"
|
||||
Delete Action = "DELETE"
|
||||
DeleteAll Action = "DELETE_ALL"
|
||||
Login Action = "LOGIN"
|
||||
LogOut Action = "LOGOUT"
|
||||
Connect Action = "CONNECT"
|
||||
Sync Action = "SYNC"
|
||||
RefreshKey Action = "REFRESH_KEY"
|
||||
RefreshAllKeys Action = "REFRESH_ALL_KEYS"
|
||||
SyncAll Action = "SYNC_ALL"
|
||||
UpgradeAll Action = "UPGRADE_ALL"
|
||||
Disconnect Action = "DISCONNECT"
|
||||
JoinHostToNet Action = "JOIN_HOST_TO_NETWORK"
|
||||
RemoveHostFromNet Action = "REMOVE_HOST_FROM_NETWORK"
|
||||
Create Action = "CREATE"
|
||||
Update Action = "UPDATE"
|
||||
Delete Action = "DELETE"
|
||||
DeleteAll Action = "DELETE_ALL"
|
||||
Login Action = "LOGIN"
|
||||
LogOut Action = "LOGOUT"
|
||||
Connect Action = "CONNECT"
|
||||
Sync Action = "SYNC"
|
||||
RefreshKey Action = "REFRESH_KEY"
|
||||
RefreshAllKeys Action = "REFRESH_ALL_KEYS"
|
||||
SyncAll Action = "SYNC_ALL"
|
||||
UpgradeAll Action = "UPGRADE_ALL"
|
||||
Disconnect Action = "DISCONNECT"
|
||||
JoinHostToNet Action = "JOIN_HOST_TO_NETWORK"
|
||||
RemoveHostFromNet Action = "REMOVE_HOST_FROM_NETWORK"
|
||||
EnableMFA Action = "ENABLE_MFA"
|
||||
DisableMFA Action = "DISABLE_MFA"
|
||||
EnforceMFA Action = "ENFORCE_MFA"
|
||||
UnenforceMFA Action = "UNENFORCE_MFA"
|
||||
EnableBasicAuth Action = "ENABLE_BASIC_AUTH"
|
||||
DisableBasicAuth Action = "DISABLE_BASIC_AUTH"
|
||||
EnableTelemetry Action = "ENABLE_TELEMETRY"
|
||||
DisableTelemetry Action = "DISABLE_TELEMETRY"
|
||||
UpdateClientSettings Action = "UPDATE_CLIENT_SETTINGS"
|
||||
UpdateAuthenticationSecuritySettings Action = "UPDATE_AUTHENTICATION_SECURITY_SETTINGS"
|
||||
UpdateMonitoringAndDebuggingSettings Action = "UPDATE_MONITORING_AND_DEBUGGING_SETTINGS"
|
||||
UpdateDisplaySettings Action = "UPDATE_DISPLAY_SETTINGS"
|
||||
UpdateAccessibilitySettings Action = "UPDATE_ACCESSIBILITY_SETTINGS"
|
||||
UpdateSMTPSettings Action = "UPDATE_EMAIL_SETTINGS"
|
||||
UpdateIDPSettings Action = "UPDATE_IDP_SETTINGS"
|
||||
)
|
||||
|
||||
type SubjectType string
|
||||
|
|
|
@ -9,39 +9,44 @@ const (
|
|||
)
|
||||
|
||||
type ServerSettings struct {
|
||||
NetclientAutoUpdate bool `json:"netclientautoupdate"`
|
||||
Verbosity int32 `json:"verbosity"`
|
||||
AuthProvider string `json:"authprovider"`
|
||||
OIDCIssuer string `json:"oidcissuer"`
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
SyncEnabled bool `json:"sync_enabled"`
|
||||
GoogleAdminEmail string `json:"google_admin_email"`
|
||||
GoogleSACredsJson string `json:"google_sa_creds_json"`
|
||||
AzureTenant string `json:"azure_tenant"`
|
||||
UserFilters []string `json:"user_filters"`
|
||||
GroupFilters []string `json:"group_filters"`
|
||||
IDPSyncInterval string `json:"idp_sync_interval"`
|
||||
Telemetry string `json:"telemetry"`
|
||||
BasicAuth bool `json:"basic_auth"`
|
||||
JwtValidityDuration int `json:"jwt_validity_duration"`
|
||||
MFAEnforced bool `json:"mfa_enforced"`
|
||||
RacRestrictToSingleNetwork bool `json:"rac_restrict_to_single_network"`
|
||||
EndpointDetection bool `json:"endpoint_detection"`
|
||||
AllowedEmailDomains string `json:"allowed_email_domains"`
|
||||
EmailSenderAddr string `json:"email_sender_addr"`
|
||||
EmailSenderUser string `json:"email_sender_user"`
|
||||
EmailSenderPassword string `json:"email_sender_password"`
|
||||
SmtpHost string `json:"smtp_host"`
|
||||
SmtpPort int `json:"smtp_port"`
|
||||
MetricInterval string `json:"metric_interval"`
|
||||
MetricsPort int `json:"metrics_port"`
|
||||
ManageDNS bool `json:"manage_dns"`
|
||||
DefaultDomain string `json:"default_domain"`
|
||||
Stun bool `json:"stun"`
|
||||
StunServers string `json:"stun_servers"`
|
||||
Theme Theme `json:"theme"`
|
||||
TextSize string `json:"text_size"`
|
||||
ReducedMotion bool `json:"reduced_motion"`
|
||||
AuditLogsRetentionPeriodInDays int `json:"audit_logs_retention_period"`
|
||||
NetclientAutoUpdate bool `json:"netclientautoupdate"`
|
||||
Verbosity int32 `json:"verbosity"`
|
||||
AuthProvider string `json:"authprovider"`
|
||||
OIDCIssuer string `json:"oidcissuer"`
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
SyncEnabled bool `json:"sync_enabled"`
|
||||
GoogleAdminEmail string `json:"google_admin_email"`
|
||||
GoogleSACredsJson string `json:"google_sa_creds_json"`
|
||||
AzureTenant string `json:"azure_tenant"`
|
||||
UserFilters []string `json:"user_filters"`
|
||||
GroupFilters []string `json:"group_filters"`
|
||||
IDPSyncInterval string `json:"idp_sync_interval"`
|
||||
Telemetry string `json:"telemetry"`
|
||||
BasicAuth bool `json:"basic_auth"`
|
||||
// JwtValidityDuration is the validity duration of auth tokens for users
|
||||
// on the dashboard (NMUI).
|
||||
JwtValidityDuration int `json:"jwt_validity_duration"`
|
||||
// JwtValidityDurationClients is the validity duration of auth tokens for
|
||||
// users on the clients (NetDesk).
|
||||
JwtValidityDurationClients int `json:"jwt_validity_duration_clients"`
|
||||
MFAEnforced bool `json:"mfa_enforced"`
|
||||
RacRestrictToSingleNetwork bool `json:"rac_restrict_to_single_network"`
|
||||
EndpointDetection bool `json:"endpoint_detection"`
|
||||
AllowedEmailDomains string `json:"allowed_email_domains"`
|
||||
EmailSenderAddr string `json:"email_sender_addr"`
|
||||
EmailSenderUser string `json:"email_sender_user"`
|
||||
EmailSenderPassword string `json:"email_sender_password"`
|
||||
SmtpHost string `json:"smtp_host"`
|
||||
SmtpPort int `json:"smtp_port"`
|
||||
MetricInterval string `json:"metric_interval"`
|
||||
MetricsPort int `json:"metrics_port"`
|
||||
ManageDNS bool `json:"manage_dns"`
|
||||
DefaultDomain string `json:"default_domain"`
|
||||
Stun bool `json:"stun"`
|
||||
StunServers string `json:"stun_servers"`
|
||||
Theme Theme `json:"theme"`
|
||||
TextSize string `json:"text_size"`
|
||||
ReducedMotion bool `json:"reduced_motion"`
|
||||
AuditLogsRetentionPeriodInDays int `json:"audit_logs_retention_period"`
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ const DefaultExpDuration = time.Minute * 5
|
|||
|
||||
// SsoState - holds SSO sign-in session data
|
||||
type SsoState struct {
|
||||
AppName string `json:"app_name"`
|
||||
Value string `json:"value"`
|
||||
Expiration time.Time `json:"expiration"`
|
||||
}
|
||||
|
|
|
@ -16,6 +16,13 @@ const (
|
|||
PLACEHOLDER_TOKEN_TEXT = "ACCESS_TOKEN"
|
||||
)
|
||||
|
||||
type FeatureFlags struct {
|
||||
EnableNetworkActivity bool `json:"enable_network_activity"`
|
||||
EnableOAuth bool `json:"enable_oauth"`
|
||||
EnableIDPIntegration bool `json:"enable_idp_integration"`
|
||||
AllowMultiServerLicense bool `json:"allow_multi_server_license"`
|
||||
}
|
||||
|
||||
// AuthParams - struct for auth params
|
||||
type AuthParams struct {
|
||||
MacAddress string `json:"macaddress"`
|
||||
|
|
|
@ -202,6 +202,16 @@ type UserAuthParams struct {
|
|||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// UserIdentityValidationRequest - user identity validation request struct
|
||||
type UserIdentityValidationRequest struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// UserIdentityValidationResponse - user identity validation response struct
|
||||
type UserIdentityValidationResponse struct {
|
||||
IdentityValidated bool `json:"identity_validated"`
|
||||
}
|
||||
|
||||
type UserTOTPVerificationParams struct {
|
||||
OTPAuthURL string `json:"otp_auth_url"`
|
||||
OTPAuthURLSignature string `json:"otp_auth_url_signature"`
|
||||
|
|
|
@ -274,14 +274,14 @@ func HandleHostCheckin(h, currentHost *models.Host) bool {
|
|||
return false
|
||||
}
|
||||
}
|
||||
ifaceDelta := len(h.Interfaces) != len(currentHost.Interfaces) ||
|
||||
ifaceDelta := len(h.Interfaces) != len(currentHost.Interfaces) || !logic.CompareIfaceSlices(h.Interfaces, currentHost.Interfaces) ||
|
||||
!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.EndpointIPv6.Equal(currentHost.EndpointIPv6))
|
||||
if ifaceDelta { // only save if something changes
|
||||
if !h.EndpointIP.Equal(currentHost.EndpointIP) || !h.EndpointIPv6.Equal(currentHost.EndpointIPv6) {
|
||||
if !h.EndpointIP.Equal(currentHost.EndpointIP) || !h.EndpointIPv6.Equal(currentHost.EndpointIPv6) || currentHost.Location == "" {
|
||||
if h.EndpointIP != nil {
|
||||
h.Location = logic.GetHostLocInfo(h.EndpointIP.String(), os.Getenv("IP_INFO_TOKEN"))
|
||||
} else if h.EndpointIPv6 != nil {
|
||||
|
@ -293,6 +293,9 @@ func HandleHostCheckin(h, currentHost *models.Host) bool {
|
|||
currentHost.Interfaces = h.Interfaces
|
||||
currentHost.DefaultInterface = h.DefaultInterface
|
||||
currentHost.NatType = h.NatType
|
||||
if h.Location != "" {
|
||||
currentHost.Location = h.Location
|
||||
}
|
||||
if h.ListenPort != 0 {
|
||||
currentHost.ListenPort = h.ListenPort
|
||||
}
|
||||
|
|
|
@ -40,13 +40,18 @@ func initAzureAD(redirectURL string, clientID string, clientSecret string) {
|
|||
}
|
||||
|
||||
func handleAzureLogin(w http.ResponseWriter, r *http.Request) {
|
||||
appName := r.Header.Get("X-Application-Name")
|
||||
if appName == "" {
|
||||
appName = logic.NetmakerDesktopApp
|
||||
}
|
||||
|
||||
var oauth_state_string = logic.RandomString(user_signin_length)
|
||||
if auth_provider == nil {
|
||||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
|
||||
if err := logic.SetState(oauth_state_string); err != nil {
|
||||
if err := logic.SetState(appName, oauth_state_string); err != nil {
|
||||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
|
@ -56,9 +61,15 @@ func handleAzureLogin(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var rState, rCode = getStateAndCode(r)
|
||||
var content, err = getAzureUserInfo(rState, rCode)
|
||||
|
||||
state, err := logic.GetState(rState)
|
||||
if err != nil {
|
||||
handleOauthNotValid(w)
|
||||
return
|
||||
}
|
||||
|
||||
content, err := getAzureUserInfo(rState, rCode)
|
||||
if err != nil {
|
||||
logger.Log(1, "error when getting user info from azure:", err.Error())
|
||||
if strings.Contains(err.Error(), "invalid oauth state") || strings.Contains(err.Error(), "failed to fetch user email from SSO state") {
|
||||
|
@ -179,7 +190,7 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
|
|||
Password: newPass,
|
||||
}
|
||||
|
||||
var jwt, jwtErr = logic.VerifyAuthRequest(authRequest)
|
||||
var jwt, jwtErr = logic.VerifyAuthRequest(authRequest, state.AppName)
|
||||
if jwtErr != nil {
|
||||
logger.Log(1, "could not parse jwt for user", authRequest.UserName)
|
||||
return
|
||||
|
|
|
@ -93,7 +93,7 @@ var htmlBaseTemplate = `<!DOCTYPE html>
|
|||
</html>`
|
||||
|
||||
var oauthNotConfigured = fmt.Sprintf(htmlBaseTemplate, `<h2>Your Netmaker server does not have OAuth configured.</h2>
|
||||
<p>Please visit the docs <a href="https://docs.netmaker.io/docs/server-installation/integrating-oauth" target="_blank" rel="noopener">here</a> to learn how to.</p>`)
|
||||
<p>Please visit the docs <a href="https://docs.netmaker.io/docs/server-installation/identity-provider-integration-guide" target="_blank" rel="noopener">here</a> to learn how to.</p>`)
|
||||
|
||||
var oauthStateInvalid = fmt.Sprintf(htmlBaseTemplate, `<h2>Invalid OAuth Session. Please re-try again.</h2>`)
|
||||
|
||||
|
|
|
@ -40,13 +40,18 @@ func initGithub(redirectURL string, clientID string, clientSecret string) {
|
|||
}
|
||||
|
||||
func handleGithubLogin(w http.ResponseWriter, r *http.Request) {
|
||||
appName := r.Header.Get("X-Application-Name")
|
||||
if appName == "" {
|
||||
appName = logic.NetmakerDesktopApp
|
||||
}
|
||||
|
||||
var oauth_state_string = logic.RandomString(user_signin_length)
|
||||
if auth_provider == nil {
|
||||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
|
||||
if err := logic.SetState(oauth_state_string); err != nil {
|
||||
if err := logic.SetState(appName, oauth_state_string); err != nil {
|
||||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
|
@ -56,9 +61,15 @@ func handleGithubLogin(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var rState, rCode = getStateAndCode(r)
|
||||
var content, err = getGithubUserInfo(rState, rCode)
|
||||
|
||||
state, err := logic.GetState(rState)
|
||||
if err != nil {
|
||||
handleOauthNotValid(w)
|
||||
return
|
||||
}
|
||||
|
||||
content, err := getGithubUserInfo(rState, rCode)
|
||||
if err != nil {
|
||||
logger.Log(1, "error when getting user info from github:", err.Error())
|
||||
if strings.Contains(err.Error(), "invalid oauth state") || strings.Contains(err.Error(), "failed to fetch user email from SSO state") {
|
||||
|
@ -170,7 +181,7 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
|
|||
Password: newPass,
|
||||
}
|
||||
|
||||
var jwt, jwtErr = logic.VerifyAuthRequest(authRequest)
|
||||
var jwt, jwtErr = logic.VerifyAuthRequest(authRequest, state.AppName)
|
||||
if jwtErr != nil {
|
||||
logger.Log(1, "could not parse jwt for user", authRequest.UserName)
|
||||
return
|
||||
|
|
|
@ -40,13 +40,18 @@ func initGoogle(redirectURL string, clientID string, clientSecret string) {
|
|||
}
|
||||
|
||||
func handleGoogleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
appName := r.Header.Get("X-Application-Name")
|
||||
if appName == "" {
|
||||
appName = logic.NetmakerDesktopApp
|
||||
}
|
||||
|
||||
var oauth_state_string = logic.RandomString(user_signin_length)
|
||||
if auth_provider == nil {
|
||||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
logger.Log(0, "Setting OAuth State ", oauth_state_string)
|
||||
if err := logic.SetState(oauth_state_string); err != nil {
|
||||
if err := logic.SetState(appName, oauth_state_string); err != nil {
|
||||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
|
@ -56,10 +61,16 @@ func handleGoogleLogin(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var rState, rCode = getStateAndCode(r)
|
||||
logger.Log(0, "Fetched OAuth State ", rState)
|
||||
var content, err = getGoogleUserInfo(rState, rCode)
|
||||
|
||||
state, err := logic.GetState(rState)
|
||||
if err != nil {
|
||||
handleOauthNotValid(w)
|
||||
return
|
||||
}
|
||||
|
||||
content, err := getGoogleUserInfo(rState, rCode)
|
||||
if err != nil {
|
||||
logger.Log(1, "error when getting user info from google:", err.Error())
|
||||
if strings.Contains(err.Error(), "invalid oauth state") {
|
||||
|
@ -162,7 +173,7 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
|
|||
Password: newPass,
|
||||
}
|
||||
|
||||
var jwt, jwtErr = logic.VerifyAuthRequest(authRequest)
|
||||
var jwt, jwtErr = logic.VerifyAuthRequest(authRequest, state.AppName)
|
||||
if jwtErr != nil {
|
||||
logger.Log(1, "could not parse jwt for user", authRequest.UserName)
|
||||
return
|
||||
|
|
|
@ -86,7 +86,7 @@ func HandleHeadlessSSOCallback(w http.ResponseWriter, r *http.Request) {
|
|||
jwt, jwtErr := logic.VerifyAuthRequest(models.UserAuthParams{
|
||||
UserName: user.UserName,
|
||||
Password: newPass,
|
||||
})
|
||||
}, logic.NetclientApp)
|
||||
if jwtErr != nil {
|
||||
logger.Log(1, "could not parse jwt for user", userClaims.getUserName())
|
||||
return
|
||||
|
|
|
@ -52,13 +52,18 @@ func initOIDC(redirectURL string, clientID string, clientSecret string, issuer s
|
|||
}
|
||||
|
||||
func handleOIDCLogin(w http.ResponseWriter, r *http.Request) {
|
||||
appName := r.Header.Get("X-Application-Name")
|
||||
if appName == "" {
|
||||
appName = logic.NetmakerDesktopApp
|
||||
}
|
||||
|
||||
var oauth_state_string = logic.RandomString(user_signin_length)
|
||||
if auth_provider == nil {
|
||||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
|
||||
if err := logic.SetState(oauth_state_string); err != nil {
|
||||
if err := logic.SetState(appName, oauth_state_string); err != nil {
|
||||
handleOauthNotConfigured(w)
|
||||
return
|
||||
}
|
||||
|
@ -67,10 +72,15 @@ func handleOIDCLogin(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func handleOIDCCallback(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var rState, rCode = getStateAndCode(r)
|
||||
|
||||
var content, err = getOIDCUserInfo(rState, rCode)
|
||||
state, err := logic.GetState(rState)
|
||||
if err != nil {
|
||||
handleOauthNotValid(w)
|
||||
return
|
||||
}
|
||||
|
||||
content, err := getOIDCUserInfo(rState, rCode)
|
||||
if err != nil {
|
||||
logger.Log(1, "error when getting user info from callback:", err.Error())
|
||||
if strings.Contains(err.Error(), "invalid oauth state") {
|
||||
|
@ -170,7 +180,7 @@ func handleOIDCCallback(w http.ResponseWriter, r *http.Request) {
|
|||
Password: newPass,
|
||||
}
|
||||
|
||||
var jwt, jwtErr = logic.VerifyAuthRequest(authRequest)
|
||||
var jwt, jwtErr = logic.VerifyAuthRequest(authRequest, state.AppName)
|
||||
if jwtErr != nil {
|
||||
logger.Log(1, "could not parse jwt for user", authRequest.UserName, jwtErr.Error())
|
||||
return
|
||||
|
|
|
@ -122,6 +122,12 @@ func syncUsers(idpUsers []idp.User) error {
|
|||
filters := logic.GetServerSettings().UserFilters
|
||||
|
||||
for _, user := range idpUsers {
|
||||
if user.AccountArchived {
|
||||
// delete the user if it has been archived.
|
||||
_ = logic.DeleteUser(user.Username)
|
||||
continue
|
||||
}
|
||||
|
||||
var found bool
|
||||
for _, filter := range filters {
|
||||
if strings.HasPrefix(user.Username, filter) {
|
||||
|
@ -150,6 +156,13 @@ func syncUsers(idpUsers []idp.User) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// It's possible that a user can attempt to log in to Netmaker
|
||||
// after the IDP is configured but before the users are synced.
|
||||
// Since the user doesn't exist, a pending user will be
|
||||
// created. Now, since the user is created, the pending user
|
||||
// can be deleted.
|
||||
_ = logic.DeletePendingUser(user.Username)
|
||||
} else if dbUser.AuthType == models.OAuth {
|
||||
if dbUser.AccountDisabled != user.AccountDisabled ||
|
||||
dbUser.DisplayName != user.DisplayName ||
|
||||
|
|
|
@ -63,7 +63,7 @@ func (g *Client) GetUsers() ([]idp.User, error) {
|
|||
var retval []idp.User
|
||||
err := g.service.Users.List().
|
||||
Customer("my_customer").
|
||||
Fields("users(id,primaryEmail,name,suspended)", "nextPageToken").
|
||||
Fields("users(id,primaryEmail,name,suspended,archived)", "nextPageToken").
|
||||
Pages(context.TODO(), func(users *admindir.Users) error {
|
||||
for _, user := range users.Users {
|
||||
retval = append(retval, idp.User{
|
||||
|
@ -71,6 +71,7 @@ func (g *Client) GetUsers() ([]idp.User, error) {
|
|||
Username: user.PrimaryEmail,
|
||||
DisplayName: user.Name.FullName,
|
||||
AccountDisabled: user.Suspended,
|
||||
AccountArchived: user.Archived,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ type User struct {
|
|||
Username string
|
||||
DisplayName string
|
||||
AccountDisabled bool
|
||||
AccountArchived bool
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
|
|
|
@ -155,7 +155,7 @@ func InitPro() {
|
|||
logic.GetFwRulesForNodeAndPeerOnGw = proLogic.GetFwRulesForNodeAndPeerOnGw
|
||||
logic.GetFwRulesForUserNodesOnGw = proLogic.GetFwRulesForUserNodesOnGw
|
||||
logic.GetHostLocInfo = proLogic.GetHostLocInfo
|
||||
|
||||
logic.GetFeatureFlags = proLogic.GetFeatureFlags
|
||||
}
|
||||
|
||||
func retrieveProLogo() string {
|
||||
|
|
|
@ -135,6 +135,8 @@ func ValidateLicense() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
proLogic.SetFeatureFlags(licenseResponse.FeatureFlags)
|
||||
|
||||
slog.Info("License validation succeeded!")
|
||||
return nil
|
||||
}
|
||||
|
@ -200,6 +202,7 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, bool
|
|||
LicenseKey: servercfg.GetLicenseKey(),
|
||||
NmServerPubKey: base64encode(publicKeyBytes),
|
||||
EncryptedPart: base64encode(encryptedData),
|
||||
NmBaseDomain: servercfg.GetNmBaseDomain(),
|
||||
}
|
||||
|
||||
requestBody, err := json.Marshal(msg)
|
||||
|
|
13
pro/logic/server.go
Normal file
13
pro/logic/server.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package logic
|
||||
|
||||
import "github.com/gravitl/netmaker/models"
|
||||
|
||||
var featureFlagsCache models.FeatureFlags
|
||||
|
||||
func SetFeatureFlags(featureFlags models.FeatureFlags) {
|
||||
featureFlagsCache = featureFlags
|
||||
}
|
||||
|
||||
func GetFeatureFlags() models.FeatureFlags {
|
||||
return featureFlagsCache
|
||||
}
|
|
@ -5,6 +5,7 @@ package pro
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -32,8 +33,9 @@ type LicenseKey struct {
|
|||
|
||||
// ValidatedLicense - the validated license struct
|
||||
type ValidatedLicense struct {
|
||||
LicenseValue string `json:"license_value" binding:"required"` // license that validation is being requested for
|
||||
EncryptedLicense string `json:"encrypted_license" binding:"required"` // to be decrypted by Netmaker using Netmaker server's private key
|
||||
LicenseValue string `json:"license_value" binding:"required"` // license that validation is being requested for
|
||||
EncryptedLicense string `json:"encrypted_license" binding:"required"` // to be decrypted by Netmaker using Netmaker server's private key
|
||||
FeatureFlags models.FeatureFlags `json:"feature_flags" binding:"required"`
|
||||
}
|
||||
|
||||
// LicenseSecret - the encrypted struct for sending user-id
|
||||
|
@ -74,6 +76,7 @@ type ValidateLicenseRequest struct {
|
|||
LicenseKey string `json:"license_key" binding:"required"`
|
||||
NmServerPubKey string `json:"nm_server_pub_key" binding:"required"` // Netmaker server public key used to send data back to Netmaker for the Netmaker server to decrypt (eg output from validating license)
|
||||
EncryptedPart string `json:"secret" binding:"required"`
|
||||
NmBaseDomain string `json:"nm_base_domain"`
|
||||
}
|
||||
|
||||
type licenseResponseCache struct {
|
||||
|
|
|
@ -50,6 +50,10 @@ func (e *Egress) UpdateEgressStatus(ctx context.Context) error {
|
|||
}).Error
|
||||
}
|
||||
|
||||
func (e *Egress) DoesEgressRouteExists(ctx context.Context) error {
|
||||
return db.FromContext(ctx).Table(e.Table()).Where("range = ?", e.Range).First(&e).Error
|
||||
}
|
||||
|
||||
func (e *Egress) Create(ctx context.Context) error {
|
||||
return db.FromContext(ctx).Table(e.Table()).Create(&e).Error
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
|||
CONFIG_PATH="$SCRIPT_DIR/$CONFIG_FILE"
|
||||
NM_QUICK_VERSION="0.1.1"
|
||||
#LATEST=$(curl -s https://api.github.com/repos/gravitl/netmaker/releases/latest | grep "tag_name" | cut -d : -f 2,3 | tr -d [:space:],\")
|
||||
LATEST=v0.99.0
|
||||
LATEST=v1.0.0
|
||||
BRANCH=master
|
||||
if [ $(id -u) -ne 0 ]; then
|
||||
echo "This script must be run as root"
|
||||
|
|
|
@ -38,82 +38,7 @@ func SetHost() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetServerConfig - gets the server config into memory from file or env
|
||||
func GetServerConfig() config.ServerConfig {
|
||||
var cfg config.ServerConfig
|
||||
cfg.APIConnString = GetAPIConnString()
|
||||
cfg.CoreDNSAddr = GetCoreDNSAddr()
|
||||
cfg.APIHost = GetAPIHost()
|
||||
cfg.APIPort = GetAPIPort()
|
||||
cfg.MasterKey = "(hidden)"
|
||||
cfg.DNSKey = "(hidden)"
|
||||
cfg.AllowedOrigin = GetAllowedOrigin()
|
||||
cfg.RestBackend = "off"
|
||||
cfg.NodeID = GetNodeID()
|
||||
cfg.BrokerType = GetBrokerType()
|
||||
cfg.EmqxRestEndpoint = GetEmqxRestEndpoint()
|
||||
if AutoUpdateEnabled() {
|
||||
cfg.NetclientAutoUpdate = "enabled"
|
||||
} else {
|
||||
cfg.NetclientAutoUpdate = "disabled"
|
||||
}
|
||||
if IsRestBackend() {
|
||||
cfg.RestBackend = "on"
|
||||
}
|
||||
cfg.DNSMode = "off"
|
||||
if IsDNSMode() {
|
||||
cfg.DNSMode = "on"
|
||||
}
|
||||
cfg.DisplayKeys = "off"
|
||||
if IsDisplayKeys() {
|
||||
cfg.DisplayKeys = "on"
|
||||
}
|
||||
cfg.DisableRemoteIPCheck = "off"
|
||||
if DisableRemoteIPCheck() {
|
||||
cfg.DisableRemoteIPCheck = "on"
|
||||
}
|
||||
cfg.Database = GetDB()
|
||||
cfg.Platform = GetPlatform()
|
||||
cfg.Version = GetVersion()
|
||||
cfg.PublicIp = GetServerHostIP()
|
||||
|
||||
// == auth config ==
|
||||
var authInfo = GetAuthProviderInfo()
|
||||
cfg.AuthProvider = authInfo[0]
|
||||
cfg.ClientID = authInfo[1]
|
||||
cfg.ClientSecret = authInfo[2]
|
||||
cfg.FrontendURL = GetFrontendURL()
|
||||
cfg.Telemetry = Telemetry()
|
||||
cfg.Server = GetServer()
|
||||
cfg.Verbosity = GetVerbosity()
|
||||
cfg.IsPro = "no"
|
||||
if IsPro {
|
||||
cfg.IsPro = "yes"
|
||||
}
|
||||
cfg.JwtValidityDuration = GetJwtValidityDuration()
|
||||
cfg.RacRestrictToSingleNetwork = GetRacRestrictToSingleNetwork()
|
||||
cfg.MetricInterval = GetMetricInterval()
|
||||
cfg.ManageDNS = GetManageDNS()
|
||||
cfg.Stun = IsStunEnabled()
|
||||
cfg.StunServers = GetStunServers()
|
||||
cfg.DefaultDomain = GetDefaultDomain()
|
||||
return cfg
|
||||
}
|
||||
|
||||
// GetJwtValidityDuration - returns the JWT validity duration in seconds
|
||||
func GetJwtValidityDuration() time.Duration {
|
||||
var defaultDuration = time.Duration(24) * time.Hour
|
||||
if os.Getenv("JWT_VALIDITY_DURATION") != "" {
|
||||
t, err := strconv.Atoi(os.Getenv("JWT_VALIDITY_DURATION"))
|
||||
if err != nil {
|
||||
return defaultDuration
|
||||
}
|
||||
return time.Duration(t) * time.Second
|
||||
}
|
||||
return defaultDuration
|
||||
}
|
||||
|
||||
// GetJwtValidityDuration - returns the JWT validity duration in seconds
|
||||
// GetJwtValidityDurationFromEnv - returns the JWT validity duration in seconds
|
||||
func GetJwtValidityDurationFromEnv() int {
|
||||
var defaultDuration = 43200
|
||||
if os.Getenv("JWT_VALIDITY_DURATION") != "" {
|
||||
|
@ -721,6 +646,10 @@ func GetEmqxRestEndpoint() string {
|
|||
|
||||
// IsBasicAuthEnabled - checks if basic auth has been configured to be turned off
|
||||
func IsBasicAuthEnabled() bool {
|
||||
if DeployedByOperator() {
|
||||
return true
|
||||
}
|
||||
|
||||
var enabled = true //default
|
||||
if os.Getenv("BASIC_AUTH") != "" {
|
||||
enabled = os.Getenv("BASIC_AUTH") == "yes"
|
||||
|
|
Loading…
Add table
Reference in a new issue