From 677d9fcc8d2eca420b7c87c8b8ad6337d2ee21af Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Thu, 29 Sep 2022 23:59:18 +0530 Subject: [PATCH] added comments --- controllers/network.go | 34 +++++---- controllers/node.go | 151 ++++++++++++++++++++-------------------- mq/dynsec.go | 70 ++++++++++++------- mq/dynsec_helper.go | 36 ++++++---- mq/mq.go | 3 +- servercfg/serverconf.go | 1 + 6 files changed, 160 insertions(+), 135 deletions(-) diff --git a/controllers/network.go b/controllers/network.go index 6cb64d97..a91b5164 100644 --- a/controllers/network.go +++ b/controllers/network.go @@ -443,19 +443,18 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) { return } // Deletes the network role from MQ - event := mq.DynSecAction{ - Payload: mq.MqDynsecPayload{ - Commands: []mq.MqDynSecCmd{ - { - Command: mq.DeleteRoleCmd, - RoleName: network, - }, + event := mq.MqDynsecPayload{ + Commands: []mq.MqDynSecCmd{ + { + Command: mq.DeleteRoleCmd, + RoleName: network, }, }, } + if err := mq.PublishEventToDynSecTopic(event); err != nil { logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v", - event.Payload.Commands, err.Error())) + event.Commands, err.Error())) } logger.Log(1, r.Header.Get("user"), "deleted network", network) w.WriteHeader(http.StatusOK) @@ -504,21 +503,20 @@ func createNetwork(w http.ResponseWriter, r *http.Request) { return } // Create Role with acls for the network - event := mq.DynSecAction{ - Payload: mq.MqDynsecPayload{ - Commands: []mq.MqDynSecCmd{ - { - Command: mq.CreateRoleCmd, - RoleName: network.NetID, - Textname: "Network wide role with Acls for nodes", - Acls: mq.FetchNetworkAcls(network.NetID), - }, + event := mq.MqDynsecPayload{ + Commands: []mq.MqDynSecCmd{ + { + Command: mq.CreateRoleCmd, + RoleName: network.NetID, + Textname: "Network wide role with Acls for nodes", + Acls: mq.FetchNetworkAcls(network.NetID), }, }, } + if err := mq.PublishEventToDynSecTopic(event); err != nil { logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v", - event.Payload.Commands, err.Error())) + event.Commands, err.Error())) } if servercfg.IsClientMode() != "off" { diff --git a/controllers/node.go b/controllers/node.go index f46857b5..e221396e 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -100,46 +100,46 @@ func authenticate(response http.ResponseWriter, request *http.Request) { logic.ReturnErrorResponse(response, request, errorResponse) return } - event := mq.DynSecAction{ - Payload: mq.MqDynsecPayload{ - Commands: []mq.MqDynSecCmd{ + // creates network role, node role,node client (added here to resolve any missing configuration in MQ) + event := mq.MqDynsecPayload{ + Commands: []mq.MqDynSecCmd{ - { - Command: mq.CreateRoleCmd, - RoleName: result.Network, - Textname: "Network wide role with Acls for nodes", - Acls: mq.FetchNetworkAcls(result.Network), - }, + { + Command: mq.CreateRoleCmd, + RoleName: result.Network, + Textname: "Network wide role with Acls for nodes", + Acls: mq.FetchNetworkAcls(result.Network), + }, - { - Command: mq.CreateRoleCmd, - RoleName: fmt.Sprintf("%s-%s", "Node", result.ID), - Acls: mq.FetchNodeAcls(result.ID), - Textname: "Role for node " + result.Name, - }, - { - Command: mq.CreateClientCmd, - Username: result.ID, - Password: authRequest.Password, - Textname: result.Name, - Roles: []mq.MqDynSecRole{ - { - Rolename: fmt.Sprintf("%s-%s", "Node", result.ID), - Priority: -1, - }, - { - Rolename: result.Network, - Priority: -1, - }, + { + Command: mq.CreateRoleCmd, + RoleName: fmt.Sprintf("%s-%s", "Node", result.ID), + Acls: mq.FetchNodeAcls(result.ID), + Textname: "Role for node " + result.Name, + }, + { + Command: mq.CreateClientCmd, + Username: result.ID, + Password: authRequest.Password, + Textname: result.Name, + Roles: []mq.MqDynSecRole{ + { + Rolename: fmt.Sprintf("%s-%s", "Node", result.ID), + Priority: -1, + }, + { + Rolename: result.Network, + Priority: -1, }, - Groups: make([]mq.MqDynSecGroup, 0), }, + Groups: make([]mq.MqDynSecGroup, 0), }, }, } + if err := mq.PublishEventToDynSecTopic(event); err != nil { logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v", - event.Payload.Commands, err.Error())) + event.Commands, err.Error())) errorResponse.Code = http.StatusInternalServerError errorResponse.Message = fmt.Sprintf("could not create mq client for node [%s]: %v", result.ID, err) return @@ -662,53 +662,51 @@ func createNode(w http.ResponseWriter, r *http.Request) { return } // Delete Any Existing Client with this ID. - event := mq.DynSecAction{ - Payload: mq.MqDynsecPayload{ - Commands: []mq.MqDynSecCmd{ - { - Command: mq.DeleteClientCmd, - Username: node.ID, - }, + event := mq.MqDynsecPayload{ + Commands: []mq.MqDynSecCmd{ + { + Command: mq.DeleteClientCmd, + Username: node.ID, }, }, } + if err := mq.PublishEventToDynSecTopic(event); err != nil { logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v", - event.Payload.Commands, err.Error())) + event.Commands, err.Error())) } // Create client for this node in Mq - event = mq.DynSecAction{ - Payload: mq.MqDynsecPayload{ - Commands: []mq.MqDynSecCmd{ - { - Command: mq.CreateRoleCmd, - RoleName: fmt.Sprintf("%s-%s", "Node", node.ID), - Acls: mq.FetchNodeAcls(node.ID), - Textname: "Role for node " + node.Name, - }, - { - Command: mq.CreateClientCmd, - Username: node.ID, - Password: nodePassword, - Textname: node.Name, - Roles: []mq.MqDynSecRole{ - { - Rolename: fmt.Sprintf("%s-%s", "Node", node.ID), - Priority: -1, - }, - { - Rolename: node.Network, - Priority: -1, - }, + event = mq.MqDynsecPayload{ + Commands: []mq.MqDynSecCmd{ + { + Command: mq.CreateRoleCmd, + RoleName: fmt.Sprintf("%s-%s", "Node", node.ID), + Acls: mq.FetchNodeAcls(node.ID), + Textname: "Role for node " + node.Name, + }, + { + Command: mq.CreateClientCmd, + Username: node.ID, + Password: nodePassword, + Textname: node.Name, + Roles: []mq.MqDynSecRole{ + { + Rolename: fmt.Sprintf("%s-%s", "Node", node.ID), + Priority: -1, + }, + { + Rolename: node.Network, + Priority: -1, }, - Groups: make([]mq.MqDynSecGroup, 0), }, + Groups: make([]mq.MqDynSecGroup, 0), }, }, } + if err := mq.PublishEventToDynSecTopic(event); err != nil { logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v", - event.Payload.Commands, err.Error())) + event.Commands, err.Error())) } response := models.NodeGet{ @@ -1046,24 +1044,23 @@ func deleteNode(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - - event := mq.DynSecAction{ - Payload: mq.MqDynsecPayload{ - Commands: []mq.MqDynSecCmd{ - { - Command: mq.DeleteRoleCmd, - RoleName: fmt.Sprintf("%s-%s", "Node", nodeid), - }, - { - Command: mq.DeleteClientCmd, - Username: nodeid, - }, + // deletes node related role and client + event := mq.MqDynsecPayload{ + Commands: []mq.MqDynSecCmd{ + { + Command: mq.DeleteRoleCmd, + RoleName: fmt.Sprintf("%s-%s", "Node", nodeid), + }, + { + Command: mq.DeleteClientCmd, + Username: nodeid, }, }, } + if err := mq.PublishEventToDynSecTopic(event); err != nil { logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v", - event.Payload.Commands, err.Error())) + event.Commands, err.Error())) } logic.ReturnSuccessResponse(w, r, nodeid+" deleted.") logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"]) diff --git a/mq/dynsec.go b/mq/dynsec.go index 41304fca..9ce66b49 100644 --- a/mq/dynsec.go +++ b/mq/dynsec.go @@ -18,38 +18,53 @@ import ( "golang.org/x/crypto/pbkdf2" ) -const DynamicSecSubTopic = "$CONTROL/dynamic-security/#" -const DynamicSecPubTopic = "$CONTROL/dynamic-security/v1" +// DynamicSecSubTopic - constant for dynamic security subscription topic +const dynamicSecSubTopic = "$CONTROL/dynamic-security/#" +// DynamicSecPubTopic - constant for dynamic security subscription topic +const dynamicSecPubTopic = "$CONTROL/dynamic-security/v1" + +// mq client for admin var mqAdminClient mqtt.Client -var ( - CreateClientCmd = "createClient" +const ( + // constant for client command + CreateClientCmd = "createClient" + // constant for disable command DisableClientCmd = "disableClient" - DeleteClientCmd = "deleteClient" - ModifyClientCmd = "modifyClient" -) + // constant for delete client command + DeleteClientCmd = "deleteClient" + // constant for modify client command + ModifyClientCmd = "modifyClient" -var ( + // constant for create role command CreateRoleCmd = "createRole" + // constant for delete role command DeleteRoleCmd = "deleteRole" ) +const ( + // constant for admin user name + mqAdminUserName = "Netmaker-Admin" + // constant for server user name + mqNetmakerServerUserName = "Netmaker-Server" + // constant for exporter user name + mqExporterUserName = "Netmaker-Exporter" +) + +// struct for dynamic security file type dynJSON struct { Clients []client `json:"clients"` Roles []role `json:"roles"` DefaultAcl defaultAccessAcl `json:"defaultACLAccess"` } -var ( - mqAdminUserName string = "Netmaker-Admin" - mqNetmakerServerUserName string = "Netmaker-Server" - mqExporterUserName string = "Netmaker-Exporter" -) - +// struct for client role type clientRole struct { Rolename string `json:"rolename"` } + +// struct for MQ client type client struct { Username string `json:"username"` TextName string `json:"textName"` @@ -59,11 +74,13 @@ type client struct { Roles []clientRole `json:"roles"` } +// struct for MQ role type role struct { Rolename string `json:"rolename"` Acls []Acl `json:"acls"` } +// struct for default acls type defaultAccessAcl struct { PublishClientSend bool `json:"publishClientSend"` PublishClientReceive bool `json:"publishClientReceive"` @@ -71,22 +88,19 @@ type defaultAccessAcl struct { Unsubscribe bool `json:"unsubscribe"` } -type dynCnf struct { - Clients []client `json:"clients"` - Roles []role `json:"roles"` - DefaultACLAccess defaultAccessAcl `json:"defaultACLAccess"` -} - +// MqDynSecGroup - struct for MQ client group type MqDynSecGroup struct { Groupname string `json:"groupname"` Priority int `json:"priority"` } +// MqDynSecRole - struct for MQ client role type MqDynSecRole struct { Rolename string `json:"rolename"` Priority int `json:"priority"` } +// Acl - struct for MQ acls type Acl struct { AclType string `json:"acltype"` Topic string `json:"topic"` @@ -94,6 +108,7 @@ type Acl struct { Allow bool `json:"allow"` } +// MqDynSecCmd - struct for MQ dynamic security command type MqDynSecCmd struct { Command string `json:"command"` Username string `json:"username"` @@ -107,19 +122,18 @@ type MqDynSecCmd struct { Roles []MqDynSecRole `json:"roles"` } -type DynSecAction struct { - Payload MqDynsecPayload -} - +// MqDynsecPayload - struct for dynamic security command payload type MqDynsecPayload struct { Commands []MqDynSecCmd `json:"commands"` } +// encodePasswordToPBKDF2 - encodes the given password with PBKDF2 hashing for MQ 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) } +// Configure - configures the dynamic initial configuration for MQ func Configure() error { if servercfg.Is_EE { dynConfig.Clients = append(dynConfig.Clients, exporterMQClient) @@ -155,14 +169,15 @@ func Configure() error { return os.WriteFile(path, data, 0755) } -func PublishEventToDynSecTopic(event DynSecAction) error { +// PublishEventToDynSecTopic - publishes the message to dynamic security topic +func PublishEventToDynSecTopic(payload MqDynsecPayload) error { - d, err := json.Marshal(event.Payload) + 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 := 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 { @@ -172,6 +187,7 @@ func PublishEventToDynSecTopic(event DynSecAction) error { return connecterr } +// 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()))) diff --git a/mq/dynsec_helper.go b/mq/dynsec_helper.go index 42a16f5a..3df1eb05 100644 --- a/mq/dynsec_helper.go +++ b/mq/dynsec_helper.go @@ -10,15 +10,21 @@ import ( "github.com/gravitl/netmaker/servercfg" ) -var ( - AdminRole string = "admin" - ServerRole string = "server" - ExporterRole string = "exporter" +const ( + // constant for admin role + adminRole = "admin" + // constant for server role + serverRole = "server" + // constant for exporter role + exporterRole = "exporter" + + // const for dynamic security file + dynamicSecurityFile = "dynamic-security.json" ) var ( - dynamicSecurityFile = "dynamic-security.json" - dynConfig = dynJSON{ + // default configuration of dynamic security + dynConfig = dynJSON{ Clients: []client{ { Username: mqAdminUserName, @@ -28,7 +34,7 @@ var ( Iterations: 0, Roles: []clientRole{ { - Rolename: AdminRole, + Rolename: adminRole, }, }, }, @@ -40,14 +46,14 @@ var ( Iterations: 0, Roles: []clientRole{ { - Rolename: ServerRole, + Rolename: serverRole, }, }, }, }, Roles: []role{ { - Rolename: AdminRole, + Rolename: adminRole, Acls: []Acl{ { AclType: "publishClientSend", @@ -106,7 +112,7 @@ var ( }, }, { - Rolename: ServerRole, + Rolename: serverRole, Acls: []Acl{ { AclType: "publishClientSend", @@ -169,12 +175,12 @@ var ( Iterations: 101, Roles: []clientRole{ { - Rolename: ExporterRole, + Rolename: exporterRole, }, }, } exporterMQRole = role{ - Rolename: ExporterRole, + Rolename: exporterRole, Acls: []Acl{ { AclType: "publishClientReceive", @@ -186,6 +192,7 @@ var ( } ) +// DynListCLientsCmdResp - struct for list clients response from MQ type DynListCLientsCmdResp struct { Responses []struct { Command string `json:"command"` @@ -194,11 +201,13 @@ type DynListCLientsCmdResp struct { } `json:"responses"` } +// ListClientsData - struct for list clients data type ListClientsData struct { Clients []string `json:"clients"` TotalCount int `json:"totalCount"` } +// GetAdminClient - fetches admin client of the MQ func GetAdminClient() (mqtt.Client, error) { opts := mqtt.NewClientOptions() setMqOptions(mqAdminUserName, servercfg.GetMqAdminPassword(), opts) @@ -214,6 +223,7 @@ func GetAdminClient() (mqtt.Client, error) { return mqclient, connecterr } +// ListClients - to list all clients in the MQ func ListClients(client mqtt.Client) (ListClientsData, error) { respChan := make(chan mqtt.Message, 10) defer close(respChan) @@ -254,6 +264,7 @@ func ListClients(client mqtt.Client) (ListClientsData, error) { return resp, errors.New("resp not found") } +// FetchNetworkAcls - fetches network acls func FetchNetworkAcls(network string) []Acl { return []Acl{ { @@ -269,6 +280,7 @@ func FetchNetworkAcls(network string) []Acl { } } +// FetchNodeAcls -fetches node acls func FetchNodeAcls(nodeID string) []Acl { return []Acl{ diff --git a/mq/mq.go b/mq/mq.go index d47b3b6d..64db9307 100644 --- a/mq/mq.go +++ b/mq/mq.go @@ -22,12 +22,13 @@ var peer_force_send = 0 var mqclient mqtt.Client +// SetUpAdminClient - sets up admin client for the MQ func SetUpAdminClient() { opts := mqtt.NewClientOptions() setMqOptions(mqAdminUserName, servercfg.GetMqAdminPassword(), opts) mqAdminClient = mqtt.NewClient(opts) opts.SetOnConnectHandler(func(client mqtt.Client) { - if token := client.Subscribe(DynamicSecSubTopic, 0, mqtt.MessageHandler(watchDynSecTopic)); token.WaitTimeout(MQ_TIMEOUT*time.Second) && token.Error() != nil { + if token := client.Subscribe(dynamicSecSubTopic, 0, mqtt.MessageHandler(watchDynSecTopic)); token.WaitTimeout(MQ_TIMEOUT*time.Second) && token.Error() != nil { client.Disconnect(240) logger.Log(0, "Dynamic security client subscription failed") } diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go index c3696be7..f9c08aa0 100644 --- a/servercfg/serverconf.go +++ b/servercfg/serverconf.go @@ -622,6 +622,7 @@ func GetMQServerPort() string { return port } +// GetMqAdminPassword - fetches the MQ Admin password func GetMqAdminPassword() string { password := "" if os.Getenv("MQ_ADMIN_PASSWORD") != "" {