diff --git a/README.md b/README.md index 5213f9b8b..4267b31af 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ If you're just looking to use Netmaker, you can create an account for free at [n # Self-Hosted Quick Start -These are the instructions for deploying a Netmaker server on your own cloud VM as quickly as possible. For more detailed instructions, visit the [Install Docs](https://netmaker.readthedocs.io/en/master/install.html). +These are the instructions for deploying a Netmaker server on your own cloud VM as quickly as possible. For more detailed instructions, visit the [Install Docs](https://docs.netmaker.io/install.html). 1. Get a cloud VM with Ubuntu 22.04 and a public IP. 2. Open ports 443, 80, 3479, 8089 and 51821-51830/udp on the VM firewall and in cloud security settings. @@ -68,7 +68,7 @@ This script gives you the option to deploy the Community or Enterprise version o

-After installing Netmaker, check out the [Walkthrough](https://itnext.io/getting-started-with-netmaker-a-wireguard-virtual-networking-platform-3d563fbd87f0) and [Getting Started](https://netmaker.readthedocs.io/en/master/getting-started.html) guides to learn more about configuring networks. Or, check out some of our other [Tutorials](https://www.netmaker.io/blog) for different use cases, including Kubernetes. +After installing Netmaker, check out the [Walkthrough](https://itnext.io/getting-started-with-netmaker-a-wireguard-virtual-networking-platform-3d563fbd87f0) and [Getting Started](https://docs.netmaker.io/getting-started.html) guides to learn more about configuring networks. Or, check out some of our other [Tutorials](https://www.netmaker.io/blog) for different use cases, including Kubernetes. # Get Support diff --git a/controllers/server.go b/controllers/server.go index 841552928..c939706ed 100644 --- a/controllers/server.go +++ b/controllers/server.go @@ -5,6 +5,7 @@ import ( "net/http" "strings" "syscall" + "time" "github.com/gorilla/mux" "golang.org/x/exp/slog" @@ -109,22 +110,36 @@ func getUsage(w http.ResponseWriter, _ *http.Request) { // 200: serverConfigResponse func getStatus(w http.ResponseWriter, r *http.Request) { type status struct { - DB bool `json:"db_connected"` - Broker bool `json:"broker_connected"` - LicenseError string `json:"license_error"` - IsPro bool `json:"is_pro"` + DB bool `json:"db_connected"` + Broker bool `json:"broker_connected"` + LicenseError string `json:"license_error"` + IsPro bool `json:"is_pro"` + TrialEndDate time.Time `json:"trial_end_date"` + IsOnTrialLicense bool `json:"is_on_trial_license"` } licenseErr := "" if servercfg.ErrLicenseValidation != nil { licenseErr = servercfg.ErrLicenseValidation.Error() } - + var trialEndDate time.Time + var err error + isOnTrial := false + if servercfg.IsPro && (servercfg.GetLicenseKey() == "" || servercfg.GetNetmakerTenantID() == "") { + trialEndDate, err = logic.GetTrialEndDate() + if err != nil { + slog.Error("failed to get trial end date", "error", err) + } else { + isOnTrial = true + } + } currentServerStatus := status{ - DB: database.IsConnected(), - Broker: mq.IsConnected(), - LicenseError: licenseErr, - IsPro: servercfg.IsPro, + DB: database.IsConnected(), + Broker: mq.IsConnected(), + LicenseError: licenseErr, + IsPro: servercfg.IsPro, + TrialEndDate: trialEndDate, + IsOnTrialLicense: isOnTrial, } w.Header().Set("Content-Type", "application/json") diff --git a/database/database.go b/database/database.go index c51340ab3..dc6385b34 100644 --- a/database/database.go +++ b/database/database.go @@ -124,29 +124,29 @@ func InitializeDatabase() error { } func createTables() { - createTable(NETWORKS_TABLE_NAME) - createTable(NODES_TABLE_NAME) - createTable(CERTS_TABLE_NAME) - createTable(DELETED_NODES_TABLE_NAME) - createTable(USERS_TABLE_NAME) - createTable(DNS_TABLE_NAME) - createTable(EXT_CLIENT_TABLE_NAME) - createTable(PEERS_TABLE_NAME) - createTable(SERVERCONF_TABLE_NAME) - createTable(SERVER_UUID_TABLE_NAME) - createTable(GENERATED_TABLE_NAME) - createTable(NODE_ACLS_TABLE_NAME) - createTable(SSO_STATE_CACHE) - createTable(METRICS_TABLE_NAME) - createTable(NETWORK_USER_TABLE_NAME) - createTable(USER_GROUPS_TABLE_NAME) - createTable(CACHE_TABLE_NAME) - createTable(HOSTS_TABLE_NAME) - createTable(ENROLLMENT_KEYS_TABLE_NAME) - createTable(HOST_ACTIONS_TABLE_NAME) + CreateTable(NETWORKS_TABLE_NAME) + CreateTable(NODES_TABLE_NAME) + CreateTable(CERTS_TABLE_NAME) + CreateTable(DELETED_NODES_TABLE_NAME) + CreateTable(USERS_TABLE_NAME) + CreateTable(DNS_TABLE_NAME) + CreateTable(EXT_CLIENT_TABLE_NAME) + CreateTable(PEERS_TABLE_NAME) + CreateTable(SERVERCONF_TABLE_NAME) + CreateTable(SERVER_UUID_TABLE_NAME) + CreateTable(GENERATED_TABLE_NAME) + CreateTable(NODE_ACLS_TABLE_NAME) + CreateTable(SSO_STATE_CACHE) + CreateTable(METRICS_TABLE_NAME) + CreateTable(NETWORK_USER_TABLE_NAME) + CreateTable(USER_GROUPS_TABLE_NAME) + CreateTable(CACHE_TABLE_NAME) + CreateTable(HOSTS_TABLE_NAME) + CreateTable(ENROLLMENT_KEYS_TABLE_NAME) + CreateTable(HOST_ACTIONS_TABLE_NAME) } -func createTable(tableName string) error { +func CreateTable(tableName string) error { return getCurrentDB()[CREATE_TABLE].(func(string) error)(tableName) } @@ -194,7 +194,7 @@ func DeleteAllRecords(tableName string) error { if err != nil { return err } - err = createTable(tableName) + err = CreateTable(tableName) if err != nil { return err } diff --git a/logic/peers.go b/logic/peers.go index 8359c35d5..c63ea2867 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -24,6 +24,10 @@ var ( ResetFailedOverPeer = func(failedOverNode *models.Node) error { return nil } + // GetFailOverPeerIps - gets failover peerips + GetFailOverPeerIps = func(peer, node *models.Node) []net.IPNet { + return []net.IPNet{} + } ) // GetPeerUpdateForHost - gets the consolidated peer update for the host from all networks @@ -144,16 +148,18 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, getExtpeersExtraRoutes(peer.Network)...) } _, isFailOverPeer := node.FailOverPeers[peer.ID.String()] - if (node.IsRelayed && node.RelayedBy != peer.ID.String()) || - (peer.IsRelayed && peer.RelayedBy != node.ID.String()) || isFailOverPeer { - // if node is relayed and peer is not the relay, set remove to true - if _, ok := peerIndexMap[peerHost.PublicKey.String()]; ok { + if servercfg.IsPro { + if (node.IsRelayed && node.RelayedBy != peer.ID.String()) || + (peer.IsRelayed && peer.RelayedBy != node.ID.String()) || isFailOverPeer { + // if node is relayed and peer is not the relay, set remove to true + if _, ok := peerIndexMap[peerHost.PublicKey.String()]; ok { + continue + } + peerConfig.Remove = true + hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig) + peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1 continue } - peerConfig.Remove = true - hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig) - peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1 - continue } uselocal := false @@ -362,31 +368,6 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet return allowedips } -func GetFailOverPeerIps(peer, node *models.Node) []net.IPNet { - allowedips := []net.IPNet{} - for failOverpeerID := range node.FailOverPeers { - failOverpeer, err := GetNodeByID(failOverpeerID) - if err == nil && failOverpeer.FailedOverBy == peer.ID { - if failOverpeer.Address.IP != nil { - allowed := net.IPNet{ - IP: failOverpeer.Address.IP, - Mask: net.CIDRMask(32, 32), - } - allowedips = append(allowedips, allowed) - } - if failOverpeer.Address6.IP != nil { - allowed := net.IPNet{ - IP: failOverpeer.Address6.IP, - Mask: net.CIDRMask(128, 128), - } - allowedips = append(allowedips, allowed) - } - - } - } - return allowedips -} - func GetEgressIPs(peer *models.Node) []net.IPNet { peerHost, err := GetHost(peer.HostID.String()) diff --git a/logic/serverconf.go b/logic/serverconf.go index c307c29d5..de369c689 100644 --- a/logic/serverconf.go +++ b/logic/serverconf.go @@ -2,6 +2,8 @@ package logic import ( "encoding/json" + "time" + "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/servercfg" ) @@ -19,46 +21,16 @@ var ( EgressesLimit = 1000000000 // FreeTier - specifies if free tier FreeTier = false + + GetTrialEndDate = func() (time.Time, error) { + return time.Time{}, nil + } ) type serverData struct { PrivateKey string `json:"privatekey,omitempty" bson:"privatekey,omitempty"` } -// StorePrivKey - stores server client WireGuard privatekey if needed -func StorePrivKey(serverID string, privateKey string) error { - var newData = serverData{} - var err error - var data []byte - newData.PrivateKey = privateKey - data, err = json.Marshal(&newData) - if err != nil { - return err - } - return database.Insert(serverID, string(data), database.SERVERCONF_TABLE_NAME) -} - -// FetchPrivKey - fetches private key -func FetchPrivKey(serverID string) (string, error) { - var dbData string - var err error - var fetchedData = serverData{} - dbData, err = database.FetchRecord(database.SERVERCONF_TABLE_NAME, serverID) - if err != nil { - return "", err - } - err = json.Unmarshal([]byte(dbData), &fetchedData) - if err != nil { - return "", err - } - return fetchedData.PrivateKey, nil -} - -// RemovePrivKey - removes a private key -func RemovePrivKey(serverID string) error { - return database.DeleteRecord(database.SERVERCONF_TABLE_NAME, serverID) -} - // FetchJWTSecret - fetches jwt secret from db func FetchJWTSecret() (string, error) { var dbData string diff --git a/logic/telemetry.go b/logic/telemetry.go index e4d480304..12b7035c6 100644 --- a/logic/telemetry.go +++ b/logic/telemetry.go @@ -32,12 +32,12 @@ func sendTelemetry() error { return nil } - var telRecord, err = fetchTelemetryRecord() + var telRecord, err = FetchTelemetryRecord() if err != nil { return err } // get telemetry data - d, err := fetchTelemetryData() + d, err := FetchTelemetryData() if err != nil { return err } @@ -71,8 +71,8 @@ func sendTelemetry() error { }) } -// fetchTelemetry - fetches telemetry data: count of various object types in DB -func fetchTelemetryData() (telemetryData, error) { +// FetchTelemetryData - fetches telemetry data: count of various object types in DB +func FetchTelemetryData() (telemetryData, error) { var data telemetryData data.IsPro = servercfg.IsPro @@ -138,8 +138,8 @@ func getClientCount(nodes []models.Node) clientCount { return count } -// fetchTelemetryRecord - get the existing UUID and Timestamp from the DB -func fetchTelemetryRecord() (models.Telemetry, error) { +// FetchTelemetryRecord - get the existing UUID and Timestamp from the DB +func FetchTelemetryRecord() (models.Telemetry, error) { var rawData string var telObj models.Telemetry var err error diff --git a/logic/timer.go b/logic/timer.go index 2d0fbb6e8..db36f5792 100644 --- a/logic/timer.go +++ b/logic/timer.go @@ -3,11 +3,12 @@ package logic import ( "context" "fmt" - "github.com/gravitl/netmaker/logger" - "golang.org/x/exp/slog" "sync" "time" + "github.com/gravitl/netmaker/logger" + "golang.org/x/exp/slog" + "github.com/gravitl/netmaker/models" ) @@ -24,7 +25,7 @@ var HookManagerCh = make(chan models.HookDetails, 3) // TimerCheckpoint - Checks if 24 hours has passed since telemetry was last sent. If so, sends telemetry data to posthog func TimerCheckpoint() error { // get the telemetry record in the DB, which contains a timestamp - telRecord, err := fetchTelemetryRecord() + telRecord, err := FetchTelemetryRecord() if err != nil { return err } diff --git a/logic/traffic.go b/logic/traffic.go index 596bd7370..3c065c29e 100644 --- a/logic/traffic.go +++ b/logic/traffic.go @@ -2,7 +2,7 @@ package logic // RetrievePrivateTrafficKey - retrieves private key of server func RetrievePrivateTrafficKey() ([]byte, error) { - var telRecord, err = fetchTelemetryRecord() + var telRecord, err = FetchTelemetryRecord() if err != nil { return nil, err } @@ -12,7 +12,7 @@ func RetrievePrivateTrafficKey() ([]byte, error) { // RetrievePublicTrafficKey - retrieves public key of server func RetrievePublicTrafficKey() ([]byte, error) { - var telRecord, err = fetchTelemetryRecord() + var telRecord, err = FetchTelemetryRecord() if err != nil { return nil, err } diff --git a/pro/initialize.go b/pro/initialize.go index 32c89857c..5481e5072 100644 --- a/pro/initialize.go +++ b/pro/initialize.go @@ -4,7 +4,10 @@ package pro import ( + "time" + controller "github.com/gravitl/netmaker/controllers" + "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/mq" @@ -31,21 +34,56 @@ func InitPro() { ) logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() { // == License Handling == - ClearLicenseCache() - if err := ValidateLicense(); err != nil { - slog.Error(err.Error()) - return + enableLicenseHook := false + licenseKeyValue := servercfg.GetLicenseKey() + netmakerTenantID := servercfg.GetNetmakerTenantID() + if licenseKeyValue != "" && netmakerTenantID != "" { + enableLicenseHook = true + } + if !enableLicenseHook { + err := initTrial() + if err != nil { + logger.Log(0, "failed to init trial", err.Error()) + enableLicenseHook = true + } + trialEndDate, err := getTrialEndDate() + if err != nil { + slog.Error("failed to get trial end date", "error", err) + enableLicenseHook = true + } else { + // check if trial ended + if time.Now().After(trialEndDate) { + // trial ended already + enableLicenseHook = true + } + } + } - slog.Info("proceeding with Paid Tier license") - logic.SetFreeTierForTelemetry(false) - // == End License Handling == - AddLicenseHooks() + + if enableLicenseHook { + logger.Log(0, "starting license checker") + ClearLicenseCache() + if err := ValidateLicense(); err != nil { + slog.Error(err.Error()) + return + } + logger.Log(0, "proceeding with Paid Tier license") + logic.SetFreeTierForTelemetry(false) + // == End License Handling == + AddLicenseHooks() + } else { + logger.Log(0, "starting trial license hook") + addTrialLicenseHook() + } + if servercfg.GetServerConfig().RacAutoDisable { AddRacHooks() } + }) logic.ResetFailOver = proLogic.ResetFailOver logic.ResetFailedOverPeer = proLogic.ResetFailedOverPeer + logic.GetFailOverPeerIps = proLogic.GetFailOverPeerIps logic.DenyClientNodeAccess = proLogic.DenyClientNode logic.IsClientNodeAllowed = proLogic.IsClientNodeAllowed logic.AllowClientNodeAccess = proLogic.RemoveDeniedNodeFromClient @@ -63,6 +101,7 @@ func InitPro() { logic.RelayUpdates = proLogic.RelayUpdates logic.IsInternetGw = proLogic.IsInternetGw logic.SetInternetGw = proLogic.SetInternetGw + logic.GetTrialEndDate = getTrialEndDate mq.UpdateMetrics = proLogic.MQUpdateMetrics mq.UpdateMetricsFallBack = proLogic.MQUpdateMetricsFallBack } diff --git a/pro/logic/failover.go b/pro/logic/failover.go index d8006334b..c3a94c207 100644 --- a/pro/logic/failover.go +++ b/pro/logic/failover.go @@ -2,6 +2,7 @@ package logic import ( "errors" + "net" "github.com/google/uuid" "github.com/gravitl/netmaker/logic" @@ -96,3 +97,29 @@ func ResetFailOver(failOverNode *models.Node) error { } return nil } + +// GetFailOverPeerIps - adds the failedOvered peerIps by the peer +func GetFailOverPeerIps(peer, node *models.Node) []net.IPNet { + allowedips := []net.IPNet{} + for failOverpeerID := range node.FailOverPeers { + failOverpeer, err := logic.GetNodeByID(failOverpeerID) + if err == nil && failOverpeer.FailedOverBy == peer.ID { + if failOverpeer.Address.IP != nil { + allowed := net.IPNet{ + IP: failOverpeer.Address.IP, + Mask: net.CIDRMask(32, 32), + } + allowedips = append(allowedips, allowed) + } + if failOverpeer.Address6.IP != nil { + allowed := net.IPNet{ + IP: failOverpeer.Address6.IP, + Mask: net.CIDRMask(128, 128), + } + allowedips = append(allowedips, allowed) + } + + } + } + return allowedips +} diff --git a/pro/trial.go b/pro/trial.go new file mode 100644 index 000000000..149120900 --- /dev/null +++ b/pro/trial.go @@ -0,0 +1,161 @@ +package pro + +import ( + "crypto/rand" + "encoding/json" + "errors" + "time" + + "github.com/gravitl/netmaker/database" + "github.com/gravitl/netmaker/logger" + "github.com/gravitl/netmaker/logic" + "github.com/gravitl/netmaker/models" + "github.com/gravitl/netmaker/netclient/ncutils" + "github.com/gravitl/netmaker/servercfg" + "golang.org/x/crypto/nacl/box" +) + +type TrialInfo struct { + PrivKey []byte `json:"priv_key"` + PubKey []byte `json:"pub_key"` + Secret []byte `json:"secret"` +} + +func addTrialLicenseHook() { + logic.HookManagerCh <- models.HookDetails{ + Hook: TrialLicenseHook, + Interval: time.Hour, + } +} + +type TrialDates struct { + TrialStartedAt time.Time `json:"trial_started_at"` + TrialEndsAt time.Time `json:"trial_ends_at"` +} + +const trial_table_name = "trial" + +const trial_data_key = "trialdata" + +// stores trial end date +func initTrial() error { + telData, err := logic.FetchTelemetryData() + if err != nil { + return err + } + if telData.Hosts > 0 || telData.Networks > 0 || telData.Users > 0 { + return nil // database is already populated, so skip creating trial + } + database.CreateTable(trial_table_name) + records, err := database.FetchRecords(trial_table_name) + if err != nil && !database.IsEmptyRecord(err) { + return err + } + if len(records) > 0 { + return nil + } + // setup encryption keys + trafficPubKey, trafficPrivKey, err := box.GenerateKey(rand.Reader) // generate traffic keys + if err != nil { + return err + } + tPriv, err := ncutils.ConvertKeyToBytes(trafficPrivKey) + if err != nil { + return err + } + + tPub, err := ncutils.ConvertKeyToBytes(trafficPubKey) + if err != nil { + return err + } + trialDates := TrialDates{ + TrialStartedAt: time.Now(), + TrialEndsAt: time.Now().Add(time.Hour * 24 * 14), + } + t := TrialInfo{ + PrivKey: tPriv, + PubKey: tPub, + } + tel, err := logic.FetchTelemetryRecord() + if err != nil { + return err + } + + trialDatesData, err := json.Marshal(trialDates) + if err != nil { + return err + } + telePubKey, err := ncutils.ConvertBytesToKey(tel.TrafficKeyPub) + if err != nil { + return err + } + trialDatesSecret, err := ncutils.BoxEncrypt(trialDatesData, telePubKey, trafficPrivKey) + if err != nil { + return err + } + t.Secret = trialDatesSecret + trialData, err := json.Marshal(t) + if err != nil { + return err + } + err = database.Insert(trial_data_key, string(trialData), trial_table_name) + if err != nil { + return err + } + return nil +} + +// TrialLicenseHook - hook func to check if pro trial has ended +func TrialLicenseHook() error { + endDate, err := getTrialEndDate() + if err != nil { + logger.FatalLog0("failed to trial end date", err.Error()) + } + if time.Now().After(endDate) { + logger.Log(0, "***IMPORTANT: Your Trial Has Ended, to continue using pro version, please visit https://app.netmaker.io/ and create on-prem tenant to obtain a license***\nIf you wish to downgrade to community version, please run this command `/root/nm-quick.sh -d`") + err = errors.New("your trial has ended") + servercfg.ErrLicenseValidation = err + return err + } + return nil +} + +// get trial date +func getTrialEndDate() (time.Time, error) { + record, err := database.FetchRecord(trial_table_name, trial_data_key) + if err != nil { + return time.Time{}, err + } + var trialInfo TrialInfo + err = json.Unmarshal([]byte(record), &trialInfo) + if err != nil { + return time.Time{}, err + } + tel, err := logic.FetchTelemetryRecord() + if err != nil { + return time.Time{}, err + } + telePrivKey, err := ncutils.ConvertBytesToKey(tel.TrafficKeyPriv) + if err != nil { + return time.Time{}, err + } + trialPubKey, err := ncutils.ConvertBytesToKey(trialInfo.PubKey) + if err != nil { + return time.Time{}, err + } + // decrypt secret + secretDecrypt, err := ncutils.BoxDecrypt(trialInfo.Secret, trialPubKey, telePrivKey) + if err != nil { + return time.Time{}, err + } + trialDates := TrialDates{} + err = json.Unmarshal(secretDecrypt, &trialDates) + if err != nil { + return time.Time{}, err + } + if trialDates.TrialEndsAt.IsZero() { + return time.Time{}, errors.New("invalid date") + } + return trialDates.TrialEndsAt, nil + +} diff --git a/scripts/nm-quick.sh b/scripts/nm-quick.sh index c5a07fe12..6e44ddcfe 100755 --- a/scripts/nm-quick.sh +++ b/scripts/nm-quick.sh @@ -11,64 +11,25 @@ if [ $(id -u) -ne 0 ]; then echo "This script must be run as root" exit 1 fi - +# increase the timeouts +export DOCKER_CLIENT_TIMEOUT=120 +export COMPOSE_HTTP_TIMEOUT=120 unset INSTALL_TYPE -unset BUILD_TYPE unset BUILD_TAG unset IMAGE_TAG -unset AUTO_BUILD unset NETMAKER_BASE_DOMAIN - +unset UPGRADE_FLAG # usage - displays usage instructions usage() { echo "nm-quick.sh v$NM_QUICK_VERSION" - echo "usage: ./nm-quick.sh [-e] [-b buildtype] [-t tag] [-a auto] [-d domain]" - echo " -e if specified, will install netmaker pro" - echo " -b type of build; options:" - echo " \"version\" - will install a specific version of Netmaker using remote git and dockerhub" - echo " \"local\": - will install by cloning repo and building images from git" - echo " \"branch\": - will install a specific branch using remote git and dockerhub" - echo " -t tag of build; if buildtype=version, tag=version. If builtype=branch or builtype=local, tag=branch" - echo " -a auto-build; skip prompts and use defaults, if none provided" - echo " -d domain; if specified, will use this domain instead of auto-generating one" - echo "examples:" - echo " nm-quick.sh -e -b version -t $LATEST" - echo " nm-quick.sh -e -b local -t feature_v0.17.2_newfeature" - echo " nm-quick.sh -e -b branch -t develop" - echo " nm-quick.sh -e -b version -t $LATEST -a -d example.com" + echo "usage: ./nm-quick.sh [-c]" + echo " -c if specified, will install netmaker community version" + echo " -u if specified, will upgrade netmaker to pro version" + echo " -d if specified, will downgrade netmaker to community version" exit 1 } -while getopts evab:d:t: flag; do - case "${flag}" in - e) - INSTALL_TYPE="pro" - UPGRADE_FLAG="yes" - ;; - v) - usage - exit 0 - ;; - a) - AUTO_BUILD="on" - ;; - b) - BUILD_TYPE=${OPTARG} - if [[ ! "$BUILD_TYPE" =~ ^(version|local|branch)$ ]]; then - echo "error: $BUILD_TYPE is invalid" - echo "valid options: version, local, branch" - usage - exit 1 - fi - ;; - t) - BUILD_TAG=${OPTARG} - ;; - d) - NETMAKER_BASE_DOMAIN=${OPTARG} - ;; - esac -done + # print_logo - prints the netmaker logo print_logo() { @@ -93,32 +54,12 @@ EOF # set_buildinfo - sets the information based on script input for how the installation should be run set_buildinfo() { - if [ -z "$BUILD_TYPE" ]; then - BUILD_TYPE="version" - BUILD_TAG=$LATEST - fi - - if [ -z "$BUILD_TAG" ] && [ "$BUILD_TYPE" = "version" ]; then - BUILD_TAG=$LATEST - fi - - if [ -z "$BUILD_TAG" ] && [ ! -z "$BUILD_TYPE" ]; then - echo "error: must specify build tag when build type \"$BUILD_TYPE\" is specified" - usage - exit 1 - fi + + BUILD_TAG=$LATEST IMAGE_TAG=$(sed 's/\//-/g' <<<"$BUILD_TAG") - if [ "$1" = "ce" ]; then - INSTALL_TYPE="ce" - elif [ "$1" = "pro" ]; then - INSTALL_TYPE="pro" - fi - - if [ "$AUTO_BUILD" = "on" ] && [ -z "$INSTALL_TYPE" ]; then - INSTALL_TYPE="ce" - elif [ -z "$INSTALL_TYPE" ]; then + if [ -z "$INSTALL_TYPE" ]; then echo "-----------------------------------------------------" echo "Would you like to install Netmaker Community Edition (CE), or Netmaker Enterprise Edition (pro)?" echo "pro will require you to create an account at https://app.netmaker.io" @@ -141,7 +82,6 @@ set_buildinfo() { fi echo "-----------Build Options-----------------------------" echo " Pro or CE: $INSTALL_TYPE" - echo " Build Type: $BUILD_TYPE" echo " Build Tag: $BUILD_TAG" echo " Image Tag: $IMAGE_TAG" echo " Installer: v$NM_QUICK_VERSION" @@ -173,7 +113,9 @@ install_yq() { setup_netclient() { set +e - netclient uninstall + if [ -x "$(command -v netclient)" ]; then + netclient uninstall + fi set -e wget -qO netclient https://github.com/gravitl/netclient/releases/download/$LATEST/netclient-linux-$ARCH @@ -257,9 +199,6 @@ wait_seconds() { ( # confirm - get user input to confirm that they want to perform the next step confirm() { ( - if [ "$AUTO_BUILD" = "on" ]; then - return 0 - fi while true; do read -p 'Does everything look right? [y/n]: ' yn case $yn in @@ -277,28 +216,26 @@ confirm() { ( done ) } + save_config() { ( echo "Saving the config to $CONFIG_PATH" touch "$CONFIG_PATH" - save_config_item NM_EMAIL "$EMAIL" - save_config_item NM_DOMAIN "$NETMAKER_BASE_DOMAIN" - save_config_item UI_IMAGE_TAG "$IMAGE_TAG" - if [ "$BUILD_TYPE" = "local" ]; then - save_config_item UI_IMAGE_TAG "$LATEST" - else - save_config_item UI_IMAGE_TAG "$IMAGE_TAG" + if [ -n "$EMAIL" ]; then + save_config_item NM_EMAIL "$EMAIL" + fi + if [ -n "$NETMAKER_BASE_DOMAIN" ]; then + save_config_item NM_DOMAIN "$NETMAKER_BASE_DOMAIN" fi + save_config_item UI_IMAGE_TAG "$IMAGE_TAG" # version-specific entries if [ "$INSTALL_TYPE" = "pro" ]; then save_config_item NETMAKER_TENANT_ID "$TENANT_ID" save_config_item LICENSE_KEY "$LICENSE_KEY" - save_config_item METRICS_EXPORTER "on" - save_config_item PROMETHEUS "on" - if [ "$BUILD_TYPE" = "version" ]; then - save_config_item SERVER_IMAGE_TAG "$IMAGE_TAG-ee" - else - save_config_item SERVER_IMAGE_TAG "$IMAGE_TAG" + if [ "$UPGRADE_FLAG" = "yes" ];then + save_config_item METRICS_EXPORTER "on" + save_config_item PROMETHEUS "on" fi + save_config_item SERVER_IMAGE_TAG "$IMAGE_TAG-ee" else save_config_item METRICS_EXPORTER "off" save_config_item PROMETHEUS "off" @@ -309,7 +246,7 @@ save_config() { ( "INSTALL_TYPE" "NODE_ID" "DNS_MODE" "NETCLIENT_AUTO_UPDATE" "API_PORT" "CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "VERBOSITY" "DEBUG_MODE" "REST_BACKEND" "DISABLE_REMOTE_IP_CHECK" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET" - "FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE") + "FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE" "CACHING_ENABLED") for name in "${toCopy[@]}"; do save_config_item $name "${!name}" done @@ -345,38 +282,7 @@ save_config_item() { ( fi ); } -# local_install_setup - builds artifacts based on specified branch locally to use in install -local_install_setup() { ( - if test -z "$NM_SKIP_CLONE"; then - rm -rf netmaker-tmp - mkdir netmaker-tmp - cd netmaker-tmp - git clone --single-branch --depth=1 --branch=$BUILD_TAG https://www.github.com/gravitl/netmaker - else - cd netmaker-tmp - echo "Skipping git clone on NM_SKIP_CLONE" - fi - cd netmaker - if test -z "$NM_SKIP_BUILD"; then - docker build --no-cache --build-arg version=$IMAGE_TAG -t gravitl/netmaker:$IMAGE_TAG . - else - echo "Skipping build on NM_SKIP_BUILD" - fi - cp compose/docker-compose.yml "$SCRIPT_DIR/docker-compose.yml" - if [ "$INSTALL_TYPE" = "pro" ]; then - cp compose/docker-compose.ee.yml "$SCRIPT_DIR/docker-compose.override.yml" - cp docker/Caddyfile-pro "$SCRIPT_DIR/Caddyfile" - else - cp docker/Caddyfile "$SCRIPT_DIR/Caddyfile" - fi - cp scripts/netmaker.default.env "$SCRIPT_DIR/netmaker.default.env" - cp docker/mosquitto.conf "$SCRIPT_DIR/mosquitto.conf" - cp docker/wait.sh "$SCRIPT_DIR/wait.sh" - cd ../../ - if test -z "$NM_SKIP_CLONE"; then - rm -rf netmaker-tmp - fi -); } + # install_dependencies - install necessary packages to run netmaker install_dependencies() { @@ -520,27 +426,23 @@ set_install_vars() { echo "For this reason, we STRONGLY RECOMMEND using your own domain. Using the auto-generated domain may lead to a failed installation due to rate limiting." echo "-----------------------------------------------------" - if [ "$AUTO_BUILD" = "on" ]; then - DOMAIN_TYPE="auto" - else - select domain_option in "Auto Generated ($NETMAKER_BASE_DOMAIN)" "Custom Domain (e.x: netmaker.example.com)"; do - case $REPLY in - 1) - echo "using $NETMAKER_BASE_DOMAIN for base domain" - DOMAIN_TYPE="auto" - break - ;; - 2) - read -p "Enter Custom Domain (make sure *.domain points to $SERVER_HOST first): " domain - NETMAKER_BASE_DOMAIN=$domain - echo "using $NETMAKER_BASE_DOMAIN" - DOMAIN_TYPE="custom" - break - ;; - *) echo "invalid option $REPLY" ;; - esac - done - fi + select domain_option in "Auto Generated ($NETMAKER_BASE_DOMAIN)" "Custom Domain (e.x: netmaker.example.com)"; do + case $REPLY in + 1) + echo "using $NETMAKER_BASE_DOMAIN for base domain" + DOMAIN_TYPE="auto" + break + ;; + 2) + read -p "Enter Custom Domain (make sure *.domain points to $SERVER_HOST first): " domain + NETMAKER_BASE_DOMAIN=$domain + echo "using $NETMAKER_BASE_DOMAIN" + DOMAIN_TYPE="custom" + break + ;; + *) echo "invalid option $REPLY" ;; + esac + done wait_seconds 2 @@ -550,7 +452,7 @@ set_install_vars() { echo " api.$NETMAKER_BASE_DOMAIN" echo " broker.$NETMAKER_BASE_DOMAIN" - if [ "$INSTALL_TYPE" = "pro" ]; then + if [ "$UPGRADE_FLAG" = "yes" ]; then echo " prometheus.$NETMAKER_BASE_DOMAIN" echo " netmaker-exporter.$NETMAKER_BASE_DOMAIN" echo " grafana.$NETMAKER_BASE_DOMAIN" @@ -565,33 +467,13 @@ set_install_vars() { wait_seconds 1 - if [ "$INSTALL_TYPE" = "pro" ]; then - - echo "-----------------------------------------------------" - echo "Provide Details for pro installation:" - echo " 1. Log into https://app.netmaker.io" - echo " 2. follow instructions to get a license at: https://docs.netmaker.io/ee/ee-setup.html" - echo " 3. Retrieve License and Tenant ID" - echo " 4. note email address" - echo "-----------------------------------------------------" - unset LICENSE_KEY - while [ -z "$LICENSE_KEY" ]; do - read -p "License Key: " LICENSE_KEY - done - unset TENANT_ID - while [ -z ${TENANT_ID} ]; do - read -p "Tenant ID: " TENANT_ID - done - fi unset GET_EMAIL unset RAND_EMAIL RAND_EMAIL="$(echo $RANDOM | md5sum | head -c 16)@email.com" # suggest the prev email or a random one EMAIL_SUGGESTED=${NM_EMAIL:-$RAND_EMAIL} - if [ -z $AUTO_BUILD ]; then - read -p "Email Address for Domain Registration (click 'enter' to use $EMAIL_SUGGESTED): " GET_EMAIL - fi + read -p "Email Address for Domain Registration (click 'enter' to use $EMAIL_SUGGESTED): " GET_EMAIL if [ -z "$GET_EMAIL" ]; then EMAIL="$EMAIL_SUGGESTED" if [ "$EMAIL" = "$NM_EMAIL" ]; then @@ -609,9 +491,8 @@ set_install_vars() { unset GET_MQ_PASSWORD unset CONFIRM_MQ_PASSWORD echo "Enter Credentials For MQ..." - if [ -z $AUTO_BUILD ]; then - read -p "MQ Username (click 'enter' to use 'netmaker'): " GET_MQ_USERNAME - fi + + read -p "MQ Username (click 'enter' to use 'netmaker'): " GET_MQ_USERNAME if [ -z "$GET_MQ_USERNAME" ]; then echo "using default username for mq" MQ_USERNAME="netmaker" @@ -626,33 +507,33 @@ set_install_vars() { ) fi - if [ -z $AUTO_BUILD ]; then - select domain_option in "Auto Generated / Config Password" "Input Your Own Password"; do - case $REPLY in - 1) - echo "using random password for mq" - break - ;; - 2) - while true; do - echo "Enter your Password For MQ: " - read -s GET_MQ_PASSWORD - echo "Enter your password again to confirm: " - read -s CONFIRM_MQ_PASSWORD - if [ ${GET_MQ_PASSWORD} != ${CONFIRM_MQ_PASSWORD} ]; then - echo "wrong password entered, try again..." - continue - fi - MQ_PASSWORD="$GET_MQ_PASSWORD" - echo "MQ Password Saved Successfully!!" - break - done + + select domain_option in "Auto Generated / Config Password" "Input Your Own Password"; do + case $REPLY in + 1) + echo "using random password for mq" + break + ;; + 2) + while true; do + echo "Enter your Password For MQ: " + read -s GET_MQ_PASSWORD + echo "Enter your password again to confirm: " + read -s CONFIRM_MQ_PASSWORD + if [ ${GET_MQ_PASSWORD} != ${CONFIRM_MQ_PASSWORD} ]; then + echo "wrong password entered, try again..." + continue + fi + MQ_PASSWORD="$GET_MQ_PASSWORD" + echo "MQ Password Saved Successfully!!" break - ;; - *) echo "invalid option $REPLY" ;; - esac - done - fi + done + break + ;; + *) echo "invalid option $REPLY" ;; + esac + done + wait_seconds 2 @@ -662,18 +543,10 @@ set_install_vars() { echo " domain: $NETMAKER_BASE_DOMAIN" echo " email: $EMAIL" echo " public ip: $SERVER_HOST" - if [ "$INSTALL_TYPE" = "pro" ]; then - echo " license: $LICENSE_KEY" - echo " account id: $TENANT_ID" - fi echo "-----------------------------------------------------------------" echo "Confirm Settings for Installation" echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" - if [ ! "$BUILD_TYPE" = "local" ]; then - IMAGE_TAG="$LATEST" - fi - confirm } @@ -688,26 +561,24 @@ install_netmaker() { echo "Pulling config files..." - if [ "$BUILD_TYPE" = "local" ]; then - local_install_setup - else - local BASE_URL="https://raw.githubusercontent.com/gravitl/netmaker/$BUILD_TAG" - - local COMPOSE_URL="$BASE_URL/compose/docker-compose.yml" - local CADDY_URL="$BASE_URL/docker/Caddyfile" - if [ "$INSTALL_TYPE" = "pro" ]; then - local COMPOSE_OVERRIDE_URL="$BASE_URL/compose/docker-compose.pro.yml" - local CADDY_URL="$BASE_URL/docker/Caddyfile-pro" - fi - wget -qO "$SCRIPT_DIR"/docker-compose.yml $COMPOSE_URL - if test -n "$COMPOSE_OVERRIDE_URL"; then - wget -qO "$SCRIPT_DIR"/docker-compose.override.yml $COMPOSE_OVERRIDE_URL - fi - wget -qO "$SCRIPT_DIR"/Caddyfile "$CADDY_URL" - wget -qO "$SCRIPT_DIR"/netmaker.default.env "$BASE_URL/scripts/netmaker.default.env" - wget -qO "$SCRIPT_DIR"/mosquitto.conf "$BASE_URL/docker/mosquitto.conf" - wget -qO "$SCRIPT_DIR"/wait.sh "$BASE_URL/docker/wait.sh" + + local BASE_URL="https://raw.githubusercontent.com/gravitl/netmaker/$BUILD_TAG" + local COMPOSE_URL="$BASE_URL/compose/docker-compose.yml" + local CADDY_URL="$BASE_URL/docker/Caddyfile" + if [ "$INSTALL_TYPE" = "pro" ]; then + local COMPOSE_OVERRIDE_URL="$BASE_URL/compose/docker-compose.pro.yml" + local CADDY_URL="$BASE_URL/docker/Caddyfile-pro" + fi + wget -qO "$SCRIPT_DIR"/docker-compose.yml $COMPOSE_URL + if [ "$UPGRADE_FLAG" = "yes" ]; then + wget -qO "$SCRIPT_DIR"/docker-compose.override.yml $COMPOSE_OVERRIDE_URL + elif [ -a "$SCRIPT_DIR"/docker-compose.override.yml ]; then + rm -f "$SCRIPT_DIR"/docker-compose.override.yml fi + wget -qO "$SCRIPT_DIR"/Caddyfile "$CADDY_URL" + wget -qO "$SCRIPT_DIR"/netmaker.default.env "$BASE_URL/scripts/netmaker.default.env" + wget -qO "$SCRIPT_DIR"/mosquitto.conf "$BASE_URL/docker/mosquitto.conf" + wget -qO "$SCRIPT_DIR"/wait.sh "$BASE_URL/docker/wait.sh" chmod +x "$SCRIPT_DIR"/wait.sh mkdir -p /etc/netmaker @@ -718,9 +589,7 @@ install_netmaker() { echo "Starting containers..." - # increase the timeouts - export DOCKER_CLIENT_TIMEOUT=120 - export COMPOSE_HTTP_TIMEOUT=120 + # start docker and rebuild containers / networks cd "${SCRIPT_DIR}" @@ -800,6 +669,9 @@ print_success() { cleanup() { # remove the existing netclient's instance from the existing network + if ! command -v netclient >/dev/null 2>&1; then + return + fi if command -v nmctl >/dev/null 2>&1; then local node_id=$(netclient list | jq '.[0].node_id' 2>/dev/null) # trim doublequotes @@ -810,7 +682,11 @@ cleanup() { fi fi - echo "Stopping all containers..." + stop_services +} + +stop_services(){ + echo "Stopping all containers, this will take a while please wait..." local containers=("mq" "netmaker-ui" "coredns" "turn" "caddy" "netmaker" "netmaker-exporter" "prometheus" "grafana") for name in "${containers[@]}"; do local running=$(docker ps | grep -w "$name") @@ -824,61 +700,127 @@ cleanup() { done } -# 1. print netmaker logo -print_logo +upgrade() { + print_logo + unset IMAGE_TAG + unset BUILD_TAG + IMAGE_TAG=$UI_IMAGE_TAG + BUILD_TAG=$UI_IMAGE_TAG + echo "-----------------------------------------------------" + echo "Provide Details for pro installation:" + echo " 1. Log into https://app.netmaker.io" + echo " 2. follow instructions to get a license at: https://docs.netmaker.io/ee/ee-setup.html" + echo " 3. Retrieve License and Tenant ID" + echo "-----------------------------------------------------" + unset LICENSE_KEY + while [ -z "$LICENSE_KEY" ]; do + read -p "License Key: " LICENSE_KEY + done + unset TENANT_ID + while [ -z ${TENANT_ID} ]; do + read -p "Tenant ID: " TENANT_ID + done + save_config + # start docker and rebuild containers / networks + stop_services + install_netmaker +} -# read the config -if [ -f "$CONFIG_PATH" ]; then - echo "Using config: $CONFIG_PATH" - source "$CONFIG_PATH" - if [ "$UPGRADE_FLAG" = "yes" ]; then - INSTALL_TYPE="pro" +downgrade () { + print_logo + unset IMAGE_TAG + unset BUILD_TAG + IMAGE_TAG=$UI_IMAGE_TAG + BUILD_TAG=$UI_IMAGE_TAG + save_config + if [ -a "$SCRIPT_DIR"/docker-compose.override.yml ]; then + rm -f "$SCRIPT_DIR"/docker-compose.override.yml fi -fi + # start docker and rebuild containers / networks + stop_services + install_netmaker +} + + +main (){ -# 2. setup the build instructions -set_buildinfo + # read the config + if [ -f "$CONFIG_PATH" ]; then + echo "Using config: $CONFIG_PATH" + source "$CONFIG_PATH" + fi + + INSTALL_TYPE="pro" + while getopts :cudv flag; do + case "${flag}" in + c) + INSTALL_TYPE="ce" + ;; + u) + echo "upgrading to pro version..." + INSTALL_TYPE="pro" + UPGRADE_FLAG="yes" + upgrade + exit 0 + ;; + d) + echo "downgrading to community version..." + INSTALL_TYPE="ce" + downgrade + exit 0 + ;; + v) + usage + exit 0 + ;; + esac +done -set +e + # 1. print netmaker logo + print_logo -# 3. install necessary packages -install_dependencies + # 2. setup the build instructions + set_buildinfo + set +e + # 3. install necessary packages + install_dependencies -# 4. install yq if necessary -install_yq + # 4. install yq if necessary + install_yq -set -e + set -e -# 6. get user input for variables -set_install_vars + # 6. get user input for variables + set_install_vars -set +e -cleanup -set -e + set +e + cleanup + set -e -# 7. get and set config files, startup docker-compose -install_netmaker + # 7. get and set config files, startup docker-compose + install_netmaker -set +e + set +e -# 8. make sure Caddy certs are working -test_connection + # 8. make sure Caddy certs are working + test_connection -# 9. install the netmaker CLI -setup_nmctl + # 9. install the netmaker CLI + setup_nmctl -# 10. create a default mesh network for netmaker -setup_mesh + # 10. create a default mesh network for netmaker + setup_mesh -set -e + set -e -# 11. add netclient to docker-compose and start it up -setup_netclient + # 11. add netclient to docker-compose and start it up + setup_netclient -# 12. make the netclient a default host and ingress gw -configure_netclient + # 12. make the netclient a default host and ingress gw + configure_netclient -# 13. print success message -print_success + # 13. print success message + print_success +} -# cp -f /etc/skel/.bashrc /root/.bashrc +main "${@}" \ No newline at end of file