Files

908 lines
24 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package servercfg
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"time"
"github.com/gravitl/netmaker/config"
)
// EmqxBrokerType denotes the broker type for EMQX MQTT
const EmqxBrokerType = "emqx"
// Emqxdeploy - emqx deploy type
type Emqxdeploy string
var (
Version = "dev"
IsPro = false
EmqxCloudDeploy Emqxdeploy = "cloud"
EmqxOnPremDeploy Emqxdeploy = "on-prem"
)
// ErrLicenseValidation checks for errors during license validation
// of tenant in ctx.
var ErrLicenseValidation = func(ctx context.Context) error { return nil }
// SetHost - sets the host ip
func SetHost() error {
remoteip, err := GetPublicIP()
if err != nil {
return err
}
os.Setenv("SERVER_HOST", remoteip)
return nil
}
// GetJwtValidityDurationFromEnv - returns the JWT validity duration in seconds
func GetJwtValidityDurationFromEnv() int {
var defaultDuration = 43200
if os.Getenv("JWT_VALIDITY_DURATION") != "" {
t, err := strconv.Atoi(os.Getenv("JWT_VALIDITY_DURATION"))
if err == nil {
return t
}
}
return defaultDuration
}
// GetAuthRateLimitLockoutSecondsFromEnv returns how long (in seconds) an IP is blocked after
// exceeding the admin auth rate limit. Env AUTH_RATE_LIMIT_LOCKOUT_DURATION overrides the
// default of 3600 (one hour).
func GetAuthRateLimitLockoutSecondsFromEnv() int {
const defaultSecs = 3600
if os.Getenv("AUTH_RATE_LIMIT_LOCKOUT_DURATION") != "" {
t, err := strconv.Atoi(os.Getenv("AUTH_RATE_LIMIT_LOCKOUT_DURATION"))
if err == nil && t > 0 {
return t
}
}
return defaultSecs
}
// GetRacRestrictToSingleNetwork - returns whether the feature to allow simultaneous network connections via RAC is enabled
func GetRacRestrictToSingleNetwork() bool {
return os.Getenv("RAC_RESTRICT_TO_SINGLE_NETWORK") == "true"
}
// GetFrontendURL - gets the frontend url
func GetFrontendURL() string {
var frontend = ""
if os.Getenv("FRONTEND_URL") != "" {
frontend = os.Getenv("FRONTEND_URL")
} else if config.Config.Server.FrontendURL != "" {
frontend = config.Config.Server.FrontendURL
}
if frontend == "" {
return fmt.Sprintf("https://dashboard.%s", GetNmBaseDomain())
}
return frontend
}
// GetAPIConnString - gets the api connections string
func GetAPIConnString() string {
conn := ""
if os.Getenv("SERVER_API_CONN_STRING") != "" {
conn = os.Getenv("SERVER_API_CONN_STRING")
} else if config.Config.Server.APIConnString != "" {
conn = config.Config.Server.APIConnString
}
return conn
}
// SetVersion - set version of netmaker
func SetVersion(v string) {
Version = v
}
// GetVersion - version of netmaker
func GetVersion() string {
return Version
}
// GetServerHostIP - fetches server IP
func GetServerHostIP() string {
return os.Getenv("SERVER_HOST")
}
// GetDB - gets the database type
func GetDB() string {
database := "sqlite"
if os.Getenv("DATABASE") != "" {
database = os.Getenv("DATABASE")
} else if config.Config.Server.Database != "" {
database = config.Config.Server.Database
}
return database
}
// CacheEnabled - checks if cache is enabled
func CacheEnabled() bool {
caching := true
if os.Getenv("CACHING_ENABLED") == "false" {
caching = false
} else if config.Config.Server.CacheEnabled == "false" {
caching = false
}
return caching
}
// GetAPIHost - gets the api host
func GetAPIHost() string {
serverhost := "127.0.0.1"
remoteip, _ := GetPublicIP()
if os.Getenv("SERVER_HTTP_HOST") != "" {
serverhost = os.Getenv("SERVER_HTTP_HOST")
} else if config.Config.Server.APIHost != "" {
serverhost = config.Config.Server.APIHost
} else if os.Getenv("SERVER_HOST") != "" {
serverhost = os.Getenv("SERVER_HOST")
} else {
if remoteip != "" {
serverhost = remoteip
}
}
return serverhost
}
// GetAPIPort - gets the api port
func GetAPIPort() string {
apiport := "8081"
if os.Getenv("API_PORT") != "" {
apiport = os.Getenv("API_PORT")
} else if config.Config.Server.APIPort != "" {
apiport = config.Config.Server.APIPort
}
return apiport
}
// GetTcpProxyPublicPort returns the client-facing WSS port published for
// external-termination (tls_mode=proxy) gateways. Defaults to 443.
// Override with TCP_PROXY_PUBLIC_PORT (1–65535).
func GetTcpProxyPublicPort() int {
defaultPort := 443 // keep in sync with schema.TcpProxyClientPortProxy
v := strings.TrimSpace(os.Getenv("TCP_PROXY_PUBLIC_PORT"))
if v == "" {
return defaultPort
}
port, err := strconv.Atoi(v)
if err != nil || port < 1 || port > 65535 {
return defaultPort
}
return port
}
// GetCoreDNSAddr - gets the core dns address
func GetCoreDNSAddr() string {
addr, _ := GetPublicIP()
if os.Getenv("COREDNS_ADDR") != "" {
addr = os.Getenv("COREDNS_ADDR")
} else if config.Config.Server.CoreDNSAddr != "" {
addr = config.Config.Server.CoreDNSAddr
}
return addr
}
// GetPublicBrokerEndpoint - returns the public broker endpoint which shall be used by netclient
func GetPublicBrokerEndpoint() string {
if os.Getenv("BROKER_ENDPOINT") != "" {
return os.Getenv("BROKER_ENDPOINT")
} else {
return config.Config.Server.Broker
}
}
func GetSmtpHost() string {
v := ""
if fromEnv := os.Getenv("SMTP_HOST"); fromEnv != "" {
v = fromEnv
} else if fromCfg := config.Config.Server.SmtpHost; fromCfg != "" {
v = fromCfg
}
return v
}
func GetSmtpPort() int {
v := 587
if fromEnv := os.Getenv("SMTP_PORT"); fromEnv != "" {
port, err := strconv.Atoi(fromEnv)
if err == nil {
v = port
}
} else if fromCfg := config.Config.Server.SmtpPort; fromCfg != 0 {
v = fromCfg
}
return v
}
func GetSenderEmail() string {
v := ""
if fromEnv := os.Getenv("EMAIL_SENDER_ADDR"); fromEnv != "" {
v = fromEnv
} else if fromCfg := config.Config.Server.EmailSenderAddr; fromCfg != "" {
v = fromCfg
}
return v
}
func GetSenderUser() string {
v := ""
if fromEnv := os.Getenv("EMAIL_SENDER_USER"); fromEnv != "" {
v = fromEnv
} else if fromCfg := config.Config.Server.EmailSenderUser; fromCfg != "" {
v = fromCfg
}
return v
}
func GetEmaiSenderPassword() string {
v := ""
if fromEnv := os.Getenv("EMAIL_SENDER_PASSWORD"); fromEnv != "" {
v = fromEnv
} else if fromCfg := config.Config.Server.EmailSenderPassword; fromCfg != "" {
v = fromCfg
}
return v
}
// GetOwnerEmail - gets the owner email (saas)
func GetOwnerEmail() string {
return os.Getenv("SAAS_OWNER_EMAIL")
}
// GetMessageQueueEndpoint - gets the message queue endpoint
func GetMessageQueueEndpoint() (string, bool) {
host, _ := GetPublicIP()
if os.Getenv("SERVER_BROKER_ENDPOINT") != "" {
host = os.Getenv("SERVER_BROKER_ENDPOINT")
} else if config.Config.Server.ServerBrokerEndpoint != "" {
host = config.Config.Server.ServerBrokerEndpoint
} else if os.Getenv("BROKER_ENDPOINT") != "" {
host = os.Getenv("BROKER_ENDPOINT")
} else if config.Config.Server.Broker != "" {
host = config.Config.Server.Broker
} else {
host += ":1883" // default
}
return host, strings.Contains(host, "wss") || strings.Contains(host, "ssl") || strings.Contains(host, "mqtts")
}
// GetBrokerType - returns the type of MQ broker
func GetBrokerType() string {
if os.Getenv("BROKER_TYPE") != "" {
return os.Getenv("BROKER_TYPE")
} else {
return "mosquitto"
}
}
// GetMasterKey - gets the configured master key of server
func GetMasterKey() string {
key := ""
if os.Getenv("MASTER_KEY") != "" {
key = os.Getenv("MASTER_KEY")
} else if config.Config.Server.MasterKey != "" {
key = config.Config.Server.MasterKey
}
return key
}
// GetAllowedOrigin - get the allowed origin
func GetAllowedOrigin() string {
allowedorigin := "*"
if os.Getenv("CORS_ALLOWED_ORIGIN") != "" {
allowedorigin = os.Getenv("CORS_ALLOWED_ORIGIN")
} else if config.Config.Server.AllowedOrigin != "" {
allowedorigin = config.Config.Server.AllowedOrigin
}
return allowedorigin
}
// IsRestBackend - checks if rest is on or off
func IsRestBackend() bool {
isrest := true
if os.Getenv("REST_BACKEND") != "" {
if os.Getenv("REST_BACKEND") == "off" {
isrest = false
}
} else if config.Config.Server.RestBackend != "" {
if config.Config.Server.RestBackend == "off" {
isrest = false
}
}
return isrest
}
// IsMetricsExporter - checks if metrics exporter is on or off
func IsMetricsExporter() bool {
export := false
if os.Getenv("METRICS_EXPORTER") != "" {
if os.Getenv("METRICS_EXPORTER") == "on" {
export = true
}
} else if config.Config.Server.MetricsExporter != "" {
if config.Config.Server.MetricsExporter == "on" {
export = true
}
}
return export
}
// GetMetricsExporterURL returns the base URL of the netmaker-exporter HTTP API.
func GetMetricsExporterURL() string {
if v := os.Getenv("NETMAKER_METRICS_TARGET"); v != "" {
return v
}
return "http://netmaker-exporter:8085"
}
// IsMessageQueueBackend - checks if message queue is on or off
func IsMessageQueueBackend() bool {
ismessagequeue := true
if os.Getenv("MESSAGEQUEUE_BACKEND") != "" {
if os.Getenv("MESSAGEQUEUE_BACKEND") == "off" {
ismessagequeue = false
}
} else if config.Config.Server.MessageQueueBackend != "" {
if config.Config.Server.MessageQueueBackend == "off" {
ismessagequeue = false
}
}
return ismessagequeue
}
// Telemetry - checks if telemetry data should be sent
func Telemetry() string {
telemetry := "on"
if os.Getenv("TELEMETRY") == "off" {
telemetry = "off"
}
if config.Config.Server.Telemetry == "off" {
telemetry = "off"
}
return telemetry
}
// GetServer - gets the server name
func GetServer() string {
server := ""
if os.Getenv("SERVER_NAME") != "" {
server = os.Getenv("SERVER_NAME")
} else if config.Config.Server.Server != "" {
server = config.Config.Server.Server
}
return server
}
func GetVerbosity() int32 {
var verbosity = 0
var err error
if os.Getenv("VERBOSITY") != "" {
verbosity, err = strconv.Atoi(os.Getenv("VERBOSITY"))
if err != nil {
verbosity = 0
}
} else if config.Config.Server.Verbosity != 0 {
verbosity = int(config.Config.Server.Verbosity)
}
if verbosity < 0 || verbosity > 4 {
verbosity = 0
}
return int32(verbosity)
}
// AutoUpdateEnabled returns a boolean indicating whether netclient auto update is enabled or disabled
// default is enabled
func AutoUpdateEnabled() bool {
if os.Getenv("NETCLIENT_AUTO_UPDATE") == "disabled" {
return false
} else if config.Config.Server.NetclientAutoUpdate == "disabled" {
return false
}
return true
}
// IsDNSMode - should it run with DNS
func IsDNSMode() bool {
isdns := true
if os.Getenv("DNS_MODE") != "" {
if os.Getenv("DNS_MODE") == "off" {
isdns = false
}
} else if config.Config.Server.DNSMode != "" {
if config.Config.Server.DNSMode == "off" {
isdns = false
}
}
return isdns
}
// IsDisplayKeys - should server be able to display keys?
func IsDisplayKeys() bool {
isdisplay := true
if os.Getenv("DISPLAY_KEYS") != "" {
if os.Getenv("DISPLAY_KEYS") == "off" {
isdisplay = false
}
} else if config.Config.Server.DisplayKeys != "" {
if config.Config.Server.DisplayKeys == "off" {
isdisplay = false
}
}
return isdisplay
}
// DisableRemoteIPCheck - disable the remote ip check
func DisableRemoteIPCheck() bool {
disabled := false
if os.Getenv("DISABLE_REMOTE_IP_CHECK") != "" {
if os.Getenv("DISABLE_REMOTE_IP_CHECK") == "on" {
disabled = true
}
} else if config.Config.Server.DisableRemoteIPCheck != "" {
if config.Config.Server.DisableRemoteIPCheck == "on" {
disabled = true
}
}
return disabled
}
// GetPublicIP - gets public ip
func GetPublicIP() (string, error) {
endpoint := ""
var err error
iplist := []string{"https://ifconfig.me/ip", "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
iplist = append([]string{publicIpService}, iplist...)
} else if config.Config.Server.PublicIPService != "" {
publicIpService = config.Config.Server.PublicIPService
// prepend the user-specified service so it's checked first
iplist = append([]string{publicIpService}, iplist...)
}
for _, ipserver := range iplist {
client := &http.Client{
Timeout: time.Second * 10,
}
resp, err := client.Get(ipserver)
if err != nil {
continue
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
continue
}
endpoint = string(bodyBytes)
break
}
}
if endpoint == "" {
err = errors.New("public address not found")
}
return endpoint, err
}
// GetPlatform - get the system type of server
func GetPlatform() string {
platform := "linux"
if os.Getenv("PLATFORM") != "" {
platform = os.Getenv("PLATFORM")
} else if config.Config.Server.Platform != "" {
platform = config.Config.Server.Platform
}
return platform
}
// GetSQLConn - get the sql connection string
func GetSQLConn() string {
sqlconn := "http://"
if os.Getenv("SQL_CONN") != "" {
sqlconn = os.Getenv("SQL_CONN")
} else if config.Config.Server.SQLConn != "" {
sqlconn = config.Config.Server.SQLConn
}
return sqlconn
}
// GetHostName - gets the host name
func GetHostName() string {
var id string
var err error
// id = getMacAddr()
if os.Getenv("HOST_NAME") != "" {
id = os.Getenv("HOST_NAME")
} else if config.Config.Server.HostName != "" {
id = config.Config.Server.HostName
} else {
id, err = os.Hostname()
if err != nil {
return ""
}
}
return id
}
func SetNodeID(id string) {
config.Config.Server.HostName = id
}
// GetAuthProviderInfo = gets the oauth provider info
func GetAuthProviderInfo() (pi []string) {
var authProvider = ""
defer func() {
if authProvider == "oidc" {
if os.Getenv("OIDC_ISSUER") != "" {
pi = append(pi, os.Getenv("OIDC_ISSUER"))
} else if config.Config.Server.OIDCIssuer != "" {
pi = append(pi, config.Config.Server.OIDCIssuer)
} else {
pi = []string{"", "", ""}
}
}
}()
if os.Getenv("AUTH_PROVIDER") != "" && os.Getenv("CLIENT_ID") != "" && os.Getenv("CLIENT_SECRET") != "" {
authProvider = strings.ToLower(os.Getenv("AUTH_PROVIDER"))
if authProvider == "google" || authProvider == "azure-ad" || authProvider == "github" || authProvider == "oidc" {
return []string{authProvider, os.Getenv("CLIENT_ID"), os.Getenv("CLIENT_SECRET")}
} else {
authProvider = ""
}
} else if config.Config.Server.AuthProvider != "" && config.Config.Server.ClientID != "" && config.Config.Server.ClientSecret != "" {
authProvider = strings.ToLower(config.Config.Server.AuthProvider)
if authProvider == "google" || authProvider == "azure-ad" || authProvider == "github" || authProvider == "oidc" {
return []string{authProvider, config.Config.Server.ClientID, config.Config.Server.ClientSecret}
}
}
return []string{"", "", ""}
}
// GetAzureTenant - retrieve the azure tenant ID from env variable or config file
func GetAzureTenant() string {
var azureTenant = ""
if os.Getenv("AZURE_TENANT") != "" {
azureTenant = os.Getenv("AZURE_TENANT")
} else if config.Config.Server.AzureTenant != "" {
azureTenant = config.Config.Server.AzureTenant
}
return azureTenant
}
// GetMqPassword - fetches the MQ password
func GetMqPassword() string {
password := ""
if os.Getenv("MQ_PASSWORD") != "" {
password = os.Getenv("MQ_PASSWORD")
} else if config.Config.Server.MQPassword != "" {
password = config.Config.Server.MQPassword
}
return password
}
// GetMqUserName - fetches the MQ username
func GetMqUserName() string {
password := ""
if os.Getenv("MQ_USERNAME") != "" {
password = os.Getenv("MQ_USERNAME")
} else if config.Config.Server.MQUserName != "" {
password = config.Config.Server.MQUserName
}
return password
}
// GetMetricsPort - get metrics port
func GetMetricsPort() int {
p := 51821
if os.Getenv("METRICS_PORT") != "" {
pStr := os.Getenv("METRICS_PORT")
pInt, err := strconv.Atoi(pStr)
if err == nil && pInt != 0 {
p = pInt
}
}
return p
}
// GetMetricInterval - get the publish metric interval
func GetMetricIntervalInMinutes() time.Duration {
//default 15 minutes
mi := "15"
if os.Getenv("PUBLISH_METRIC_INTERVAL") != "" {
mi = os.Getenv("PUBLISH_METRIC_INTERVAL")
}
interval, err := strconv.Atoi(mi)
if err != nil {
interval = 15
}
return time.Duration(interval) * time.Minute
}
// GetMetricInterval - get the publish metric interval
func GetMetricInterval() string {
//default 15 minutes
mi := "15"
if os.Getenv("PUBLISH_METRIC_INTERVAL") != "" {
mi = os.Getenv("PUBLISH_METRIC_INTERVAL")
}
return mi
}
// GetManageDNS - if manage DNS enabled or not
func GetManageDNS() bool {
enabled := true
if os.Getenv("MANAGE_DNS") != "" {
enabled = os.Getenv("MANAGE_DNS") == "true"
}
return enabled
}
func IsOldAclEnabled() bool {
enabled := true
if os.Getenv("OLD_ACL_SUPPORT") != "" {
enabled = os.Getenv("OLD_ACL_SUPPORT") == "true"
}
return enabled
}
// GetDefaultDomain - get the default domain
func GetDefaultDomain() string {
//default hosted.nm
domain := "nm.internal"
if os.Getenv("DEFAULT_DOMAIN") != "" {
if validateDomain(os.Getenv("DEFAULT_DOMAIN")) {
domain = os.Getenv("DEFAULT_DOMAIN")
}
}
return domain
}
func validateDomain(domain string) bool {
domainPattern := `[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}(\.[a-zA-Z0-9][a-zA-Z0-9_-]{0,62})*(\.[a-zA-Z][a-zA-Z0-9]{0,10}){1}`
exp := regexp.MustCompile("^" + domainPattern + "$")
return exp.MatchString(domain)
}
// GetEmqxRestEndpoint - returns the REST API Endpoint of EMQX
func GetEmqxRestEndpoint() string {
return os.Getenv("EMQX_REST_ENDPOINT")
}
// 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"
} else if config.Config.Server.BasicAuth != "" {
enabled = config.Config.Server.BasicAuth == "yes"
}
return enabled
}
// GetLicenseKey - retrieves pro license value from env or conf files
func GetLicenseKey() string {
licenseKeyValue := os.Getenv("LICENSE_KEY")
if licenseKeyValue == "" {
licenseKeyValue = config.Config.Server.LicenseValue
}
return licenseKeyValue
}
// GetNetmakerTenantID - get's the associated, Netmaker, tenant ID to verify ownership
func GetNetmakerTenantID() string {
netmakerTenantID := os.Getenv("NETMAKER_TENANT_ID")
if netmakerTenantID == "" {
netmakerTenantID = config.Config.Server.NetmakerTenantID
}
return netmakerTenantID
}
// GetUserLimit - fetches free tier limits on users
func GetUserLimit() int {
var userslimit int
if os.Getenv("USERS_LIMIT") != "" {
userslimit, _ = strconv.Atoi(os.Getenv("USERS_LIMIT"))
} else {
userslimit = config.Config.Server.UsersLimit
}
return userslimit
}
// GetNetworkLimit - fetches free tier limits on networks
func GetNetworkLimit() int {
var networkslimit int
if os.Getenv("NETWORKS_LIMIT") != "" {
networkslimit, _ = strconv.Atoi(os.Getenv("NETWORKS_LIMIT"))
} else {
networkslimit = config.Config.Server.NetworksLimit
}
return networkslimit
}
// GetMachinesLimit - fetches free tier limits on machines (clients + hosts)
func GetMachinesLimit() int {
if l, err := strconv.Atoi(os.Getenv("MACHINES_LIMIT")); err == nil {
return l
}
return config.Config.Server.MachinesLimit
}
// GetIngressLimit - fetches free tier limits on ingresses
func GetIngressLimit() int {
if l, err := strconv.Atoi(os.Getenv("INGRESSES_LIMIT")); err == nil {
return l
}
return config.Config.Server.IngressesLimit
}
// GetEgressLimit - fetches free tier limits on egresses
func GetEgressLimit() int {
if l, err := strconv.Atoi(os.Getenv("EGRESSES_LIMIT")); err == nil {
return l
}
return config.Config.Server.EgressesLimit
}
// DeployedByOperator - returns true if the instance is deployed by netmaker operator
func DeployedByOperator() bool {
if os.Getenv("DEPLOYED_BY_OPERATOR") != "" {
return os.Getenv("DEPLOYED_BY_OPERATOR") == "true"
}
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
}
// IsStunEnabled - returns true if STUN set to on
func IsStunEnabled() bool {
var enabled = true
if os.Getenv("STUN") != "" {
enabled = os.Getenv("STUN") == "true"
}
return enabled
}
func GetStunServers() string {
stunservers := os.Getenv("STUN_SERVERS")
if stunservers == "" {
stunservers = "stun1.l.google.com:19302,stun2.l.google.com:19302,stun3.l.google.com:19302,stun4.l.google.com:19302"
}
return stunservers
}
// GetEnvironment returns the environment the server is running in (e.g. dev, staging, prod...)
func GetEnvironment() string {
if env := os.Getenv("ENVIRONMENT"); env != "" {
return env
}
if env := config.Config.Server.Environment; env != "" {
return env
}
return ""
}
// GetEmqxDeployType - fetches emqx deploy type this server uses
func GetEmqxDeployType() (deployType Emqxdeploy) {
deployType = EmqxOnPremDeploy
if os.Getenv("EMQX_DEPLOY_TYPE") == string(EmqxCloudDeploy) {
deployType = EmqxCloudDeploy
}
return
}
// GetEmqxAppID - gets the emqx cloud app id
func GetEmqxAppID() string {
return os.Getenv("EMQX_APP_ID")
}
// GetEmqxAppSecret - gets the emqx cloud app secret
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
}
// GetNmBaseDomain - fetches nm base domain
func GetNmBaseDomain() string {
return os.Getenv("NM_DOMAIN")
}
func GetGrpcEndpoint() string {
if os.Getenv("GRPC_ENDPOINT") != "" {
return os.Getenv("GRPC_ENDPOINT")
} else if config.Config.Server.GrpcEndpoint != "" {
return config.Config.Server.GrpcEndpoint
}
return fmt.Sprintf("grpc.%s", GetNmBaseDomain())
}
// IsHA - returns true if running in High Availability mode (multiple replicas)
func IsHA() bool {
return os.Getenv("IS_HA") == "true"
}
// IsMasterPod - returns true if this pod should run singleton operations
// In K8s StatefulSet HA mode, the 0th pod (hostname ending with -0) is the master.
// This ensures migrations, IDP sync, and other singleton operations only run on one pod.
// Can be overridden with IS_MASTER_POD env var for manual control.
//
// TODO: Future Enhancement - Implement Leader Election for True HA Failover
// Current limitation: If pod-0 goes down, singleton operations and MQ subscriptions
// are unavailable until pod-0 recovers. For automatic failover, implement:
//
// Option 1: K8s Lease-based Leader Election
// - Use client-go's leaderelection package
// - Any pod can become leader if current leader fails
// - Requires K8s ServiceAccount with lease permissions
//
// Option 2: Database-based Distributed Lock
// - Use database (PostgreSQL/SQLite) advisory locks
// - Leader heartbeats to maintain lock, others acquire on timeout
// - Works in non-K8s environments too
//
// Option 3: etcd/Consul-based Leader Election
// - Use distributed consensus systems
// - Most robust but adds infrastructure dependency
//
// Implementation would replace static pod-0 check with dynamic leader status.
func IsMasterPod() bool {
// Allow manual override via environment variable
if override := os.Getenv("IS_MASTER_POD"); override != "" {
return override == "true"
}
// If not in HA mode, default to true (single instance deployment)
if !IsHA() {
return true
}
// StatefulSet pods are named <statefulset-name>-<ordinal>.
// Compare the last hyphen-delimited segment to "0" to avoid
// false positives (e.g. "netmaker-10" matching a naive HasSuffix "-0").
hostname, err := os.Hostname()
if err != nil {
return true
}
parts := strings.Split(hostname, "-")
return parts[len(parts)-1] == "0"
}