| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 | package mqimport (	"crypto/sha512"	"encoding/base64"	"encoding/json"	"errors"	"fmt"	"os"	"time"	mqtt "github.com/eclipse/paho.mqtt.golang"	"github.com/gravitl/netmaker/functions"	"github.com/gravitl/netmaker/logger"	"github.com/gravitl/netmaker/logic"	"github.com/gravitl/netmaker/netclient/ncutils"	"github.com/gravitl/netmaker/servercfg"	"golang.org/x/crypto/pbkdf2")// mq client for adminvar mqAdminClient mqtt.Clientconst (	// constant for client command	CreateClientCmd = "createClient"	// constant for disable command	DisableClientCmd = "disableClient"	// constant for delete client command	DeleteClientCmd = "deleteClient"	// constant for modify client command	ModifyClientCmd = "modifyClient"	// constant for create role command	CreateRoleCmd = "createRole"	// constant for delete role command	DeleteRoleCmd = "deleteRole"	// constant for admin user name	mqAdminUserName = "Netmaker-Admin"	// constant for server user name	mqNetmakerServerUserName = "Netmaker-Server"	// constant for exporter user name	mqExporterUserName = "Netmaker-Exporter"	// DynamicSecSubTopic - constant for dynamic security subscription topic	dynamicSecSubTopic = "$CONTROL/dynamic-security/#"	// DynamicSecPubTopic - constant for dynamic security subscription topic	dynamicSecPubTopic = "$CONTROL/dynamic-security/v1")// struct for dynamic security filetype dynJSON struct {	Clients    []client         `json:"clients"`	Roles      []role           `json:"roles"`	DefaultAcl defaultAccessAcl `json:"defaultACLAccess"`}// struct for client roletype clientRole struct {	Rolename string `json:"rolename"`}// struct for MQ clienttype client struct {	Username   string       `json:"username"`	TextName   string       `json:"textName"`	Password   string       `json:"password"`	Salt       string       `json:"salt"`	Iterations int          `json:"iterations"`	Roles      []clientRole `json:"roles"`}// struct for MQ roletype role struct {	Rolename string `json:"rolename"`	Acls     []Acl  `json:"acls"`}// struct for default aclstype defaultAccessAcl struct {	PublishClientSend    bool `json:"publishClientSend"`	PublishClientReceive bool `json:"publishClientReceive"`	Subscribe            bool `json:"subscribe"`	Unsubscribe          bool `json:"unsubscribe"`}// MqDynSecGroup - struct for MQ client grouptype MqDynSecGroup struct {	Groupname string `json:"groupname"`	Priority  int    `json:"priority"`}// MqDynSecRole - struct for MQ client roletype MqDynSecRole struct {	Rolename string `json:"rolename"`	Priority int    `json:"priority"`}// Acl - struct for MQ aclstype Acl struct {	AclType  string `json:"acltype"`	Topic    string `json:"topic"`	Priority int    `json:"priority,omitempty"`	Allow    bool   `json:"allow"`}// MqDynSecCmd - struct for MQ dynamic security commandtype MqDynSecCmd struct {	Command         string          `json:"command"`	Username        string          `json:"username"`	Password        string          `json:"password"`	RoleName        string          `json:"rolename,omitempty"`	Acls            []Acl           `json:"acls,omitempty"`	Clientid        string          `json:"clientid"`	Textname        string          `json:"textname"`	Textdescription string          `json:"textdescription"`	Groups          []MqDynSecGroup `json:"groups"`	Roles           []MqDynSecRole  `json:"roles"`}// MqDynsecPayload - struct for dynamic security command payloadtype MqDynsecPayload struct {	Commands []MqDynSecCmd `json:"commands"`}// encodePasswordToPBKDF2 - encodes the given password with PBKDF2 hashing for MQfunc encodePasswordToPBKDF2(password string, salt string, iterations int, keyLength int) string {	binaryEncoded := pbkdf2.Key([]byte(password), []byte(salt), iterations, keyLength, sha512.New)	return base64.StdEncoding.EncodeToString(binaryEncoded)}// Configure - configures the dynamic initial configuration for MQfunc Configure() error {	logger.Log(0, "Configuring MQ...")	dynConfig := dynConfigInI	path := functions.GetNetmakerPath() + ncutils.GetSeparator() + dynamicSecurityFile	password := servercfg.GetMqAdminPassword()	if password == "" {		return errors.New("MQ admin password not provided")	}	if logic.CheckIfFileExists(path) {		data, err := os.ReadFile(path)		if err == nil {			var cfg dynJSON			err = json.Unmarshal(data, &cfg)			if err == nil {				logger.Log(0, "MQ config exists already, So Updating Existing Config...")				dynConfig = cfg			}		}	}	exporter := false	for i, cI := range dynConfig.Clients {		if cI.Username == mqAdminUserName || cI.Username == mqNetmakerServerUserName {			salt := logic.RandomString(12)			hashed := encodePasswordToPBKDF2(password, salt, 101, 64)			cI.Password = hashed			cI.Iterations = 101			cI.Salt = base64.StdEncoding.EncodeToString([]byte(salt))			dynConfig.Clients[i] = cI		} else if servercfg.Is_EE && cI.Username == mqExporterUserName {			exporter = true			exporterPassword := servercfg.GetLicenseKey()			salt := logic.RandomString(12)			hashed := encodePasswordToPBKDF2(exporterPassword, salt, 101, 64)			cI.Password = hashed			cI.Iterations = 101			cI.Salt = base64.StdEncoding.EncodeToString([]byte(salt))			dynConfig.Clients[i] = cI		}	}	if servercfg.Is_EE && !exporter {		exporterPassword := servercfg.GetLicenseKey()		salt := logic.RandomString(12)		hashed := encodePasswordToPBKDF2(exporterPassword, salt, 101, 64)		exporterMQClient.Password = hashed		exporterMQClient.Iterations = 101		exporterMQClient.Salt = base64.StdEncoding.EncodeToString([]byte(salt))		dynConfig.Clients = append(dynConfig.Clients, exporterMQClient)	}	data, err := json.MarshalIndent(dynConfig, "", " ")	if err != nil {		return err	}	return os.WriteFile(path, data, 0755)}// publishes the message to dynamic security topicfunc publishEventToDynSecTopic(payload MqDynsecPayload) error {	d, err := json.Marshal(payload)	if err != nil {		return err	}	var connecterr error	if token := mqAdminClient.Publish(dynamicSecPubTopic, 2, false, d); !token.WaitTimeout(MQ_TIMEOUT*time.Second) || token.Error() != nil {		if token.Error() == nil {			connecterr = errors.New("connect timeout")		} else {			connecterr = token.Error()		}	}	return connecterr}// watchDynSecTopic - message handler for dynamic security responsesfunc watchDynSecTopic(client mqtt.Client, msg mqtt.Message) {	logger.Log(1, fmt.Sprintf("----->WatchDynSecTopic Message: %+v", string(msg.Payload())))}
 |