netmaker/mq/dynsec.go

216 lines
6.5 KiB
Go
Raw Normal View History

2022-09-14 02:03:39 +08:00
package mq
import (
2022-09-26 19:27:10 +08:00
"crypto/sha512"
"encoding/base64"
2022-09-14 02:03:39 +08:00
"encoding/json"
2022-09-26 19:27:10 +08:00
"errors"
2022-09-14 02:03:39 +08:00
"fmt"
2022-09-26 19:27:10 +08:00
"os"
2022-09-30 01:24:41 +08:00
"time"
2022-09-14 02:03:39 +08:00
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/gravitl/netmaker/functions"
2022-09-14 02:03:39 +08:00
"github.com/gravitl/netmaker/logger"
2022-09-26 19:27:10 +08:00
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/netclient/ncutils"
2022-09-26 19:27:10 +08:00
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/crypto/pbkdf2"
2022-09-14 02:03:39 +08:00
)
2022-09-30 02:29:18 +08:00
// mq client for admin
2022-09-26 19:27:10 +08:00
var mqAdminClient mqtt.Client
2022-09-30 02:29:18 +08:00
const (
// constant for client command
CreateClientCmd = "createClient"
// constant for disable command
DisableClientCmd = "disableClient"
2022-09-30 02:29:18 +08:00
// constant for delete client command
DeleteClientCmd = "deleteClient"
// constant for modify client command
ModifyClientCmd = "modifyClient"
2022-09-30 02:29:18 +08:00
// constant for create role command
2022-09-30 01:24:41 +08:00
CreateRoleCmd = "createRole"
2022-09-30 02:29:18 +08:00
// constant for delete role command
2022-09-30 01:24:41 +08:00
DeleteRoleCmd = "deleteRole"
2022-09-30 02:29:18 +08:00
// constant for admin user name
mqAdminUserName = "Netmaker-Admin"
// constant for server user name
mqNetmakerServerUserName = "Netmaker-Server"
// constant for exporter user name
mqExporterUserName = "Netmaker-Exporter"
2022-10-01 08:57:30 +08:00
// DynamicSecSubTopic - constant for dynamic security subscription topic
dynamicSecSubTopic = "$CONTROL/dynamic-security/#"
// DynamicSecPubTopic - constant for dynamic security subscription topic
dynamicSecPubTopic = "$CONTROL/dynamic-security/v1"
2022-09-30 02:29:18 +08:00
)
// struct for dynamic security file
type dynJSON struct {
Clients []client `json:"clients"`
Roles []role `json:"roles"`
DefaultAcl defaultAccessAcl `json:"defaultACLAccess"`
}
2022-09-30 02:29:18 +08:00
// struct for client role
type clientRole struct {
Rolename string `json:"rolename"`
}
2022-09-30 02:29:18 +08:00
// struct for MQ client
2022-09-26 19:27:10 +08:00
type 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"`
2022-09-26 19:27:10 +08:00
}
2022-09-30 02:29:18 +08:00
// struct for MQ role
2022-09-26 19:27:10 +08:00
type role struct {
Rolename string `json:"rolename"`
Acls []Acl `json:"acls"`
2022-09-26 19:27:10 +08:00
}
2022-09-30 02:29:18 +08:00
// struct for default acls
2022-09-26 19:27:10 +08:00
type defaultAccessAcl struct {
PublishClientSend bool `json:"publishClientSend"`
PublishClientReceive bool `json:"publishClientReceive"`
Subscribe bool `json:"subscribe"`
Unsubscribe bool `json:"unsubscribe"`
}
2022-09-30 02:29:18 +08:00
// MqDynSecGroup - struct for MQ client group
2022-09-14 02:03:39 +08:00
type MqDynSecGroup struct {
Groupname string `json:"groupname"`
Priority int `json:"priority"`
}
2022-09-30 02:29:18 +08:00
// MqDynSecRole - struct for MQ client role
2022-09-14 02:03:39 +08:00
type MqDynSecRole struct {
Rolename string `json:"rolename"`
Priority int `json:"priority"`
}
2022-09-30 02:29:18 +08:00
// Acl - struct for MQ acls
type Acl struct {
AclType string `json:"acltype"`
Topic string `json:"topic"`
Priority int `json:"priority,omitempty"`
Allow bool `json:"allow"`
}
2022-09-30 02:29:18 +08:00
// MqDynSecCmd - struct for MQ dynamic security command
2022-09-14 02:03:39 +08:00
type MqDynSecCmd struct {
Command string `json:"command"`
Username string `json:"username"`
Password string `json:"password"`
RoleName string `json:"rolename,omitempty"`
Acls []Acl `json:"acls,omitempty"`
2022-09-14 02:03:39 +08:00
Clientid string `json:"clientid"`
Textname string `json:"textname"`
Textdescription string `json:"textdescription"`
Groups []MqDynSecGroup `json:"groups"`
Roles []MqDynSecRole `json:"roles"`
}
2022-09-30 02:29:18 +08:00
// MqDynsecPayload - struct for dynamic security command payload
2022-09-14 02:03:39 +08:00
type MqDynsecPayload struct {
Commands []MqDynSecCmd `json:"commands"`
}
2022-09-30 02:29:18 +08:00
// encodePasswordToPBKDF2 - encodes the given password with PBKDF2 hashing for MQ
2022-09-26 19:27:10 +08:00
func 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)
}
2022-09-30 02:29:18 +08:00
// Configure - configures the dynamic initial configuration for MQ
2022-09-26 19:27:10 +08:00
func Configure() error {
2022-10-21 14:40:12 +08:00
logger.Log(0, "Configuring MQ...")
dynConfig := dynConfigInI
path := functions.GetNetmakerPath() + ncutils.GetSeparator() + dynamicSecurityFile
2022-10-21 14:40:12 +08:00
2022-09-26 19:27:10 +08:00
password := servercfg.GetMqAdminPassword()
if password == "" {
return errors.New("MQ admin password not provided")
}
2022-10-21 14:40:12 +08:00
if logic.CheckIfFileExists(path) {
data, err := os.ReadFile(path)
if err == nil {
2022-10-21 15:54:22 +08:00
var cfg dynJSON
err = json.Unmarshal(data, &cfg)
if err == nil {
logger.Log(0, "MQ config exists already, So Updating Existing Config...")
dynConfig = cfg
}
2022-10-21 14:40:12 +08:00
}
}
2022-10-21 15:33:28 +08:00
exporter := false
for i, cI := range dynConfig.Clients {
2022-09-26 19:27:10 +08:00
if cI.Username == mqAdminUserName || cI.Username == mqNetmakerServerUserName {
salt := logic.RandomString(12)
hashed := encodePasswordToPBKDF2(password, salt, 101, 64)
cI.Password = hashed
cI.Iterations = 101
2022-09-26 19:27:10 +08:00
cI.Salt = base64.StdEncoding.EncodeToString([]byte(salt))
dynConfig.Clients[i] = cI
} else if servercfg.Is_EE && cI.Username == mqExporterUserName {
2022-10-21 15:33:28 +08:00
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
2022-09-26 19:27:10 +08:00
}
}
2022-10-21 15:33:28 +08:00
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)
dynConfig.Roles = append(dynConfig.Roles, exporterMQRole)
}
data, err := json.MarshalIndent(dynConfig, "", " ")
2022-09-26 19:27:10 +08:00
if err != nil {
return err
}
return os.WriteFile(path, data, 0755)
2022-09-26 19:27:10 +08:00
}
2022-09-30 02:29:18 +08:00
// PublishEventToDynSecTopic - publishes the message to dynamic security topic
func PublishEventToDynSecTopic(payload MqDynsecPayload) error {
2022-09-14 02:03:39 +08:00
2022-09-30 02:29:18 +08:00
d, err := json.Marshal(payload)
2022-09-27 00:25:24 +08:00
if err != nil {
return err
}
2022-09-30 01:24:41 +08:00
var connecterr error
2022-09-30 02:29:18 +08:00
if token := mqAdminClient.Publish(dynamicSecPubTopic, 2, false, d); !token.WaitTimeout(MQ_TIMEOUT*time.Second) || token.Error() != nil {
2022-09-30 01:24:41 +08:00
if token.Error() == nil {
connecterr = errors.New("connect timeout")
} else {
connecterr = token.Error()
}
2022-09-14 02:03:39 +08:00
}
2022-09-30 01:24:41 +08:00
return connecterr
2022-09-14 02:03:39 +08:00
}
2022-09-30 02:29:18 +08:00
// watchDynSecTopic - message handler for dynamic security responses
func watchDynSecTopic(client mqtt.Client, msg mqtt.Message) {
logger.Log(1, fmt.Sprintf("----->WatchDynSecTopic Message: %+v", string(msg.Payload())))
}