Skip to content

Commit

Permalink
session management for remote access client (#2592)
Browse files Browse the repository at this point in the history
* feat(NET-584): wip: session mgmt for RAC

* feat(NET-584): session mgmt for RAC

* feat(NET-584): session mgmt for RAC

* feat(NET-584): session mgmt for RAC

* feat(NET-584): session mgmt for RAC

* feat(NET-584): session mgmt for RAC

* feat(NET-584): session mgmt for RAC

* feat(NET-584): session mgmt for RAC

* feat(NET-584): only enable if client is disabled

* feat(NET-584): check only for normal users

* feat(NET-584): fix condition
  • Loading branch information
Aceix authored Oct 2, 2023
1 parent 622eb98 commit bfc61fa
Show file tree
Hide file tree
Showing 13 changed files with 238 additions and 70 deletions.
114 changes: 59 additions & 55 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package config
import (
"fmt"
"os"
"time"

"gopkg.in/yaml.v3"
)
Expand All @@ -32,61 +33,64 @@ type EnvironmentConfig struct {

// ServerConfig - server conf struct
type ServerConfig struct {
CoreDNSAddr string `yaml:"corednsaddr"`
APIConnString string `yaml:"apiconn"`
APIHost string `yaml:"apihost"`
APIPort string `yaml:"apiport"`
Broker string `yam:"broker"`
ServerBrokerEndpoint string `yaml:"serverbrokerendpoint"`
BrokerType string `yaml:"brokertype"`
EmqxRestEndpoint string `yaml:"emqxrestendpoint"`
NetclientAutoUpdate string `yaml:"netclientautoupdate"`
NetclientEndpointDetection string `yaml:"netclientendpointdetection"`
MasterKey string `yaml:"masterkey"`
DNSKey string `yaml:"dnskey"`
AllowedOrigin string `yaml:"allowedorigin"`
NodeID string `yaml:"nodeid"`
RestBackend string `yaml:"restbackend"`
MessageQueueBackend string `yaml:"messagequeuebackend"`
DNSMode string `yaml:"dnsmode"`
DisableRemoteIPCheck string `yaml:"disableremoteipcheck"`
Version string `yaml:"version"`
SQLConn string `yaml:"sqlconn"`
Platform string `yaml:"platform"`
Database string `yaml:"database"`
Verbosity int32 `yaml:"verbosity"`
AuthProvider string `yaml:"authprovider"`
OIDCIssuer string `yaml:"oidcissuer"`
ClientID string `yaml:"clientid"`
ClientSecret string `yaml:"clientsecret"`
FrontendURL string `yaml:"frontendurl"`
DisplayKeys string `yaml:"displaykeys"`
AzureTenant string `yaml:"azuretenant"`
Telemetry string `yaml:"telemetry"`
HostNetwork string `yaml:"hostnetwork"`
Server string `yaml:"server"`
PublicIPService string `yaml:"publicipservice"`
MQPassword string `yaml:"mqpassword"`
MQUserName string `yaml:"mqusername"`
MetricsExporter string `yaml:"metrics_exporter"`
BasicAuth string `yaml:"basic_auth"`
LicenseValue string `yaml:"license_value"`
NetmakerTenantID string `yaml:"netmaker_tenant_id"`
IsPro string `yaml:"is_ee" json:"IsEE"`
StunPort int `yaml:"stun_port"`
TurnServer string `yaml:"turn_server"`
TurnApiServer string `yaml:"turn_api_server"`
TurnPort int `yaml:"turn_port"`
TurnUserName string `yaml:"turn_username"`
TurnPassword string `yaml:"turn_password"`
UseTurn bool `yaml:"use_turn"`
UsersLimit int `yaml:"user_limit"`
NetworksLimit int `yaml:"network_limit"`
MachinesLimit int `yaml:"machines_limit"`
IngressesLimit int `yaml:"ingresses_limit"`
EgressesLimit int `yaml:"egresses_limit"`
DeployedByOperator bool `yaml:"deployed_by_operator"`
Environment string `yaml:"environment"`
CoreDNSAddr string `yaml:"corednsaddr"`
APIConnString string `yaml:"apiconn"`
APIHost string `yaml:"apihost"`
APIPort string `yaml:"apiport"`
Broker string `yam:"broker"`
ServerBrokerEndpoint string `yaml:"serverbrokerendpoint"`
BrokerType string `yaml:"brokertype"`
EmqxRestEndpoint string `yaml:"emqxrestendpoint"`
NetclientAutoUpdate string `yaml:"netclientautoupdate"`
NetclientEndpointDetection string `yaml:"netclientendpointdetection"`
MasterKey string `yaml:"masterkey"`
DNSKey string `yaml:"dnskey"`
AllowedOrigin string `yaml:"allowedorigin"`
NodeID string `yaml:"nodeid"`
RestBackend string `yaml:"restbackend"`
MessageQueueBackend string `yaml:"messagequeuebackend"`
DNSMode string `yaml:"dnsmode"`
DisableRemoteIPCheck string `yaml:"disableremoteipcheck"`
Version string `yaml:"version"`
SQLConn string `yaml:"sqlconn"`
Platform string `yaml:"platform"`
Database string `yaml:"database"`
Verbosity int32 `yaml:"verbosity"`
AuthProvider string `yaml:"authprovider"`
OIDCIssuer string `yaml:"oidcissuer"`
ClientID string `yaml:"clientid"`
ClientSecret string `yaml:"clientsecret"`
FrontendURL string `yaml:"frontendurl"`
DisplayKeys string `yaml:"displaykeys"`
AzureTenant string `yaml:"azuretenant"`
Telemetry string `yaml:"telemetry"`
HostNetwork string `yaml:"hostnetwork"`
Server string `yaml:"server"`
PublicIPService string `yaml:"publicipservice"`
MQPassword string `yaml:"mqpassword"`
MQUserName string `yaml:"mqusername"`
MetricsExporter string `yaml:"metrics_exporter"`
BasicAuth string `yaml:"basic_auth"`
LicenseValue string `yaml:"license_value"`
NetmakerTenantID string `yaml:"netmaker_tenant_id"`
IsPro string `yaml:"is_ee" json:"IsEE"`
StunPort int `yaml:"stun_port"`
StunList string `yaml:"stun_list"`
TurnServer string `yaml:"turn_server"`
TurnApiServer string `yaml:"turn_api_server"`
TurnPort int `yaml:"turn_port"`
TurnUserName string `yaml:"turn_username"`
TurnPassword string `yaml:"turn_password"`
UseTurn bool `yaml:"use_turn"`
UsersLimit int `yaml:"user_limit"`
NetworksLimit int `yaml:"network_limit"`
MachinesLimit int `yaml:"machines_limit"`
IngressesLimit int `yaml:"ingresses_limit"`
EgressesLimit int `yaml:"egresses_limit"`
DeployedByOperator bool `yaml:"deployed_by_operator"`
Environment string `yaml:"environment"`
JwtValidityDuration time.Duration `yaml:"jwt_validity_duration"`
RacAutoDisable bool `yaml:"rac_auto_disable"`
}

// SQLConfig - Generic SQL Config
Expand Down
29 changes: 28 additions & 1 deletion controllers/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/exp/slog"
)
Expand Down Expand Up @@ -96,7 +97,6 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
}
// Send back the JWT
successJSONResponse, jsonError := json.Marshal(successResponse)

if jsonError != nil {
logger.Log(0, username,
"error marshalling resp: ", err.Error())
Expand All @@ -106,6 +106,33 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
logger.Log(2, username, "was authenticated")
response.Header().Set("Content-Type", "application/json")
response.Write(successJSONResponse)

go func() {
if servercfg.IsPro && servercfg.GetRacAutoDisable() {
// enable all associeated clients for the user
clients, err := logic.GetAllExtClients()
if err != nil {
slog.Error("error getting clients: ", "error", err)
return
}
for _, client := range clients {
if client.OwnerID == username && !client.Enabled {
slog.Info(fmt.Sprintf("enabling ext client %s for user %s due to RAC autodisabling feature", client.ClientID, client.OwnerID))
if newClient, err := logic.ToggleExtClientConnectivity(&client, true); err != nil {
slog.Error("error disabling ext client in RAC autodisable hook", "error", err)
continue // dont return but try for other clients
} else {
// publish peer update to ingress gateway
if ingressNode, err := logic.GetNodeByID(newClient.IngressGatewayID); err == nil {
if err = mq.PublishPeerUpdate(); err != nil {
slog.Error("error updating ext clients on", "ingress", ingressNode.ID.String(), "err", err.Error())
}
}
}
}
}
}
}()
}

// swagger:route GET /api/users/adm/hassuperadmin user hasSuperAdmin
Expand Down
5 changes: 5 additions & 0 deletions logic/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {

// Create a new JWT for the node
tokenString, _ := CreateUserJWT(authRequest.UserName, result.IsSuperAdmin, result.IsAdmin)

// update last login time
result.LastLoginTime = time.Now()
UpsertUser(result)

return tokenString, nil
}

Expand Down
27 changes: 27 additions & 0 deletions logic/extpeers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/models"
"golang.org/x/exp/slog"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)

Expand Down Expand Up @@ -276,3 +277,29 @@ func GetAllExtClients() ([]models.ExtClient, error) {

return clients, nil
}

// ToggleExtClientConnectivity - enables or disables an ext client
func ToggleExtClientConnectivity(client *models.ExtClient, enable bool) (models.ExtClient, error) {
update := models.CustomExtClient{
Enabled: enable,
ClientID: client.ClientID,
PublicKey: client.PublicKey,
DNS: client.DNS,
ExtraAllowedIPs: client.ExtraAllowedIPs,
DeniedACLs: client.DeniedACLs,
RemoteAccessClientID: client.RemoteAccessClientID,
}

// update in DB
newClient := UpdateExtClient(client, &update)
if err := DeleteExtClient(client.Network, client.ClientID); err != nil {
slog.Error("failed to delete ext client during update", "id", client.ClientID, "network", client.Network, "error", err)
return newClient, err
}
if err := SaveExtClient(&newClient); err != nil {
slog.Error("failed to save updated ext client during update", "id", newClient.ClientID, "network", newClient.Network, "error", err)
return newClient, err
}

return newClient, nil
}
2 changes: 1 addition & 1 deletion logic/jwts.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func CreateJWT(uuid string, macAddress string, network string) (response string,

// CreateUserJWT - creates a user jwt token
func CreateUserJWT(username string, issuperadmin, isadmin bool) (response string, err error) {
expirationTime := time.Now().Add(60 * 12 * time.Minute)
expirationTime := time.Now().Add(servercfg.GetServerConfig().JwtValidityDuration)
claims := &models.UserClaims{
UserName: username,
IsSuperAdmin: issuperadmin,
Expand Down
2 changes: 1 addition & 1 deletion logic/timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
const timer_hours_between_runs = 24

// HookManagerCh - channel to add any new hooks
var HookManagerCh = make(chan models.HookDetails, 2)
var HookManagerCh = make(chan models.HookDetails, 3)

// == Public ==

Expand Down
20 changes: 11 additions & 9 deletions models/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,21 @@ type AuthParams struct {

// User struct - struct for Users
type User struct {
UserName string `json:"username" bson:"username" validate:"min=3,max=40,in_charset|email"`
Password string `json:"password" bson:"password" validate:"required,min=5"`
IsAdmin bool `json:"isadmin" bson:"isadmin"`
IsSuperAdmin bool `json:"issuperadmin"`
RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"`
UserName string `json:"username" bson:"username" validate:"min=3,max=40,in_charset|email"`
Password string `json:"password" bson:"password" validate:"required,min=5"`
IsAdmin bool `json:"isadmin" bson:"isadmin"`
IsSuperAdmin bool `json:"issuperadmin"`
RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"`
LastLoginTime time.Time `json:"last_login_time"`
}

// ReturnUser - return user struct
type ReturnUser struct {
UserName string `json:"username"`
IsAdmin bool `json:"isadmin"`
IsSuperAdmin bool `json:"issuperadmin"`
RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"`
UserName string `json:"username"`
IsAdmin bool `json:"isadmin"`
IsSuperAdmin bool `json:"issuperadmin"`
RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"`
LastLoginTime time.Time `json:"last_login_time"`
}

// UserAuthParams - user auth params struct
Expand Down
3 changes: 3 additions & 0 deletions pro/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ func InitPro() {
logic.SetFreeTierForTelemetry(false)
// == End License Handling ==
AddLicenseHooks()
if servercfg.GetServerConfig().RacAutoDisable {
AddRacHooks()
}
resetFailover()
})
logic.EnterpriseFailoverFunc = proLogic.SetFailover
Expand Down
76 changes: 76 additions & 0 deletions pro/remote_access_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package pro

import (
"fmt"
"time"

"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/exp/slog"
)

const racAutoDisableCheckInterval = 3 * time.Minute

// AddRacHooks - adds hooks for Remote Access Client
func AddRacHooks() {
slog.Debug("adding RAC autodisable hook")
logic.HookManagerCh <- models.HookDetails{
Hook: racAutoDisableHook,
Interval: racAutoDisableCheckInterval,
}
}

// racAutoDisableHook - checks if RAC is enabled and if it is, checks if it should be disabled
func racAutoDisableHook() error {
slog.Debug("running RAC autodisable hook")

users, err := logic.GetUsers()
if err != nil {
slog.Error("error getting users: ", "error", err)
return err
}
clients, err := logic.GetAllExtClients()
if err != nil {
slog.Error("error getting clients: ", "error", err)
return err
}

currentTime := time.Now()
validityDuration := servercfg.GetJwtValidityDuration()
for _, user := range users {
if !currentTime.After(user.LastLoginTime.Add(validityDuration)) {
continue
}
for _, client := range clients {
if (client.OwnerID == user.UserName) && !user.IsAdmin && !user.IsSuperAdmin && client.Enabled {
slog.Info(fmt.Sprintf("disabling ext client %s for user %s due to RAC autodisabling", client.ClientID, client.OwnerID))
if err := disableExtClient(&client); err != nil {
slog.Error("error disabling ext client in RAC autodisable hook", "error", err)
continue // dont return but try for other clients
}
}
}
}

slog.Debug("finished running RAC autodisable hook")
return nil
}

func disableExtClient(client *models.ExtClient) error {
if newClient, err := logic.ToggleExtClientConnectivity(client, false); err != nil {
return err
} else {
// publish peer update to ingress gateway
if ingressNode, err := logic.GetNodeByID(newClient.IngressGatewayID); err == nil {
if err = mq.PublishPeerUpdate(); err != nil {
slog.Error("error updating ext clients on", "ingress", ingressNode.ID.String(), "err", err.Error())
}
} else {
return err
}
}

return nil
}
4 changes: 4 additions & 0 deletions scripts/netmaker.default.env
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,7 @@ FRONTEND_URL=
AZURE_TENANT=
# https://oidc.yourprovider.com - URL of oidc provider
OIDC_ISSUER=
# Duration of JWT token validity in seconds
JWT_VALIDITY_DURATION=43200
# Auto disable a user's connecteds clients bassed on JWT token expiration
RAC_AUTO_DISABLE="true"
2 changes: 1 addition & 1 deletion scripts/nm-quick.sh
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ save_config() { (
"CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "STUN_PORT" "VERBOSITY"
"TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND"
"DISABLE_REMOTE_IP_CHECK" "NETCLIENT_ENDPOINT_DETECTION" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
"FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT")
"FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE")
for name in "${toCopy[@]}"; do
save_config_item $name "${!name}"
done
Expand Down
2 changes: 1 addition & 1 deletion scripts/nm-upgrade.sh
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ save_config() { (
"CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "STUN_PORT" "VERBOSITY"
"TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND"
"DISABLE_REMOTE_IP_CHECK" "NETCLIENT_ENDPOINT_DETECTION" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
"FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT")
"FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE")
for name in "${toCopy[@]}"; do
save_config_item $name "${!name}"
done
Expand Down
Loading

0 comments on commit bfc61fa

Please sign in to comment.