Browse Source

Merge pull request #1500 from gravitl/bugfix_v0.15.1_security_check

adding security fixes
Alex Feiszli 2 years ago
parent
commit
16a653d34c
5 changed files with 62 additions and 61 deletions
  1. 1 16
      controllers/node.go
  2. 42 41
      controllers/security.go
  3. 2 2
      main.go
  4. 5 0
      models/error.go
  5. 12 2
      serverctl/serverctl.go

+ 1 - 16
controllers/node.go

@@ -181,7 +181,7 @@ func nodeauth(next http.Handler) http.HandlerFunc {
 func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		var errorResponse = models.ErrorResponse{
-			Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
+			Code: http.StatusUnauthorized, Message: unauthorized_msg,
 		}
 
 		var params = mux.Vars(r)
@@ -190,9 +190,6 @@ func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Ha
 		//check that the request is for a valid network
 		//if (networkCheck && !networkexists) || err != nil {
 		if networkCheck && !networkexists {
-			errorResponse = models.ErrorResponse{
-				Code: http.StatusNotFound, Message: "W1R3: This network does not exist. ",
-			}
 			returnErrorResponse(w, r, errorResponse)
 			return
 		} else {
@@ -210,9 +207,6 @@ func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Ha
 			if len(tokenSplit) > 1 {
 				authToken = tokenSplit[1]
 			} else {
-				errorResponse = models.ErrorResponse{
-					Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.",
-				}
 				returnErrorResponse(w, r, errorResponse)
 				return
 			}
@@ -229,9 +223,6 @@ func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Ha
 			var nodeID = ""
 			username, networks, isadmin, errN := logic.VerifyUserToken(authToken)
 			if errN != nil {
-				errorResponse = models.ErrorResponse{
-					Code: http.StatusUnauthorized, Message: "W1R3: Unauthorized, Invalid Token Processed.",
-				}
 				returnErrorResponse(w, r, errorResponse)
 				return
 			}
@@ -264,9 +255,6 @@ func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Ha
 					} else {
 						node, err := logic.GetNodeByID(nodeID)
 						if err != nil {
-							errorResponse = models.ErrorResponse{
-								Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.",
-							}
 							returnErrorResponse(w, r, errorResponse)
 							return
 						}
@@ -285,9 +273,6 @@ func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Ha
 				}
 			}
 			if !isAuthorized {
-				errorResponse = models.ErrorResponse{
-					Code: http.StatusUnauthorized, Message: "W1R3: You are unauthorized to access this endpoint.",
-				}
 				returnErrorResponse(w, r, errorResponse)
 				return
 			} else {

+ 42 - 41
controllers/security.go

@@ -2,7 +2,6 @@ package controller
 
 import (
 	"encoding/json"
-	"errors"
 	"net/http"
 	"strings"
 
@@ -14,14 +13,23 @@ import (
 	"github.com/gravitl/netmaker/servercfg"
 )
 
+const (
+	master_uname     = "masteradministrator"
+	unauthorized_msg = "unauthorized"
+	unauthorized_err = models.Error(unauthorized_msg)
+)
+
 func securityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
+
 	return func(w http.ResponseWriter, r *http.Request) {
 		var errorResponse = models.ErrorResponse{
-			Code: http.StatusUnauthorized, Message: "W1R3: It's not you it's me.",
+			Code: http.StatusUnauthorized, Message: unauthorized_msg,
 		}
 
 		var params = mux.Vars(r)
 		bearerToken := r.Header.Get("Authorization")
+		// to have a custom DNS service adding entries
+		// we should refactor this, but is for the special case of an external service to query the DNS api
 		if strings.Contains(r.RequestURI, "/dns") && strings.ToUpper(r.Method) == "GET" && authenticateDNSToken(bearerToken) {
 			// do dns stuff
 			r.Header.Set("user", "nameserver")
@@ -30,19 +38,17 @@ func securityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
 			next.ServeHTTP(w, r)
 			return
 		}
-
-		networks, username, err := SecurityCheck(reqAdmin, params["networkname"], bearerToken)
+		var networkName = params["networkname"]
+		if len(networkName) == 0 {
+			networkName = params["network"]
+		}
+		networks, username, err := SecurityCheck(reqAdmin, networkName, bearerToken)
 		if err != nil {
-			if strings.Contains(err.Error(), "does not exist") {
-				errorResponse.Code = http.StatusNotFound
-			}
-			errorResponse.Message = err.Error()
 			returnErrorResponse(w, r, errorResponse)
 			return
 		}
 		networksJson, err := json.Marshal(&networks)
 		if err != nil {
-			errorResponse.Message = err.Error()
 			returnErrorResponse(w, r, errorResponse)
 			return
 		}
@@ -54,46 +60,33 @@ func securityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
 
 // SecurityCheck - checks token stuff
 func SecurityCheck(reqAdmin bool, netname string, token string) ([]string, string, error) {
-
-	var hasBearer = true
 	var tokenSplit = strings.Split(token, " ")
 	var authToken = ""
+	userNetworks := []string{}
 
 	if len(tokenSplit) < 2 {
-		hasBearer = false
+		return userNetworks, "", unauthorized_err
 	} else {
 		authToken = tokenSplit[1]
 	}
-	userNetworks := []string{}
 	//all endpoints here require master so not as complicated
-	isMasterAuthenticated := authenticateMaster(authToken)
-	username := ""
-	if !hasBearer || !isMasterAuthenticated {
-		userName, networks, isadmin, err := logic.VerifyUserToken(authToken)
-		username = userName
-		if err != nil {
-			return nil, username, errors.New("error verifying user token")
-		}
-		if !isadmin && reqAdmin {
-			return nil, username, errors.New("you are unauthorized to access this endpoint")
-		}
-		userNetworks = networks
-		if isadmin {
-			userNetworks = []string{ALL_NETWORK_ACCESS}
-		} else {
-			networkexists, err := functions.NetworkExists(netname)
-			if err != nil && !database.IsEmptyRecord(err) {
-				return nil, "", err
-			}
-			if netname != "" && !networkexists {
-				return nil, "", errors.New("this network does not exist")
-			}
-		}
-	} else if isMasterAuthenticated {
-		userNetworks = []string{ALL_NETWORK_ACCESS}
+	if authenticateMaster(authToken) {
+		return []string{ALL_NETWORK_ACCESS}, master_uname, nil
+	}
+	username, networks, isadmin, err := logic.VerifyUserToken(authToken)
+	if err != nil {
+		return nil, username, unauthorized_err
+	}
+	if !isadmin && reqAdmin {
+		return nil, username, unauthorized_err
+	}
+	userNetworks = networks
+	if isadmin {
+		return []string{ALL_NETWORK_ACCESS}, username, nil
 	}
-	if len(userNetworks) == 0 {
-		userNetworks = append(userNetworks, NO_NETWORKS_PRESENT)
+	// check network admin access
+	if len(netname) > 0 && (!authenticateNetworkUser(netname, userNetworks) || len(userNetworks) == 0) {
+		return nil, username, unauthorized_err
 	}
 	return userNetworks, username, nil
 }
@@ -103,6 +96,14 @@ func authenticateMaster(tokenString string) bool {
 	return tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != ""
 }
 
+func authenticateNetworkUser(network string, userNetworks []string) bool {
+	networkexists, err := functions.NetworkExists(network)
+	if (err != nil && !database.IsEmptyRecord(err)) || !networkexists {
+		return false
+	}
+	return logic.StringSliceContains(userNetworks, network)
+}
+
 //Consider a more secure way of setting master key
 func authenticateDNSToken(tokenString string) bool {
 	tokens := strings.Split(tokenString, " ")
@@ -115,7 +116,7 @@ func authenticateDNSToken(tokenString string) bool {
 func continueIfUserMatch(next http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		var errorResponse = models.ErrorResponse{
-			Code: http.StatusUnauthorized, Message: "W1R3: This doesn't look like you.",
+			Code: http.StatusUnauthorized, Message: unauthorized_msg,
 		}
 		var params = mux.Vars(r)
 		var requestedUser = params["username"]

+ 2 - 2
main.go

@@ -86,9 +86,9 @@ func initialize() { // Client Mode Prereq Check
 		logger.Log(0, "no OAuth provider found or not configured, continuing without OAuth")
 	}
 
-	err = serverctl.SetDefaultACLS()
+	err = serverctl.SetDefaults()
 	if err != nil {
-		logger.FatalLog("error setting default acls: ", err.Error())
+		logger.FatalLog("error setting defaults: ", err.Error())
 	}
 
 	if servercfg.IsClientMode() != "off" {

+ 5 - 0
models/error.go

@@ -0,0 +1,5 @@
+package models
+
+type Error string
+
+func (e Error) Error() string { return string(e) }

+ 12 - 2
serverctl/serverctl.go

@@ -81,14 +81,24 @@ func SyncServerNetwork(network string) error {
 	return nil
 }
 
-// SetDefaultACLS - runs through each network to see if ACL's are set. If not, goes through each node in network and adds the default ACL
-func SetDefaultACLS() error {
+func SetDefaults() error {
+	if err := setNodeDefaults(); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// setNodeDefaults - runs through each node and set defaults
+func setNodeDefaults() error {
 	// upgraded systems will not have ACL's set, which is why we need this function
 	nodes, err := logic.GetAllNodes()
 	if err != nil {
 		return err
 	}
 	for i := range nodes {
+		logic.SetNodeDefaults(&nodes[i])
+		logic.UpdateNode(&nodes[i], &nodes[i])
 		currentNodeACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(nodes[i].Network), nodeacls.NodeID(nodes[i].ID))
 		if (err != nil && (database.IsEmptyRecord(err) || strings.Contains(err.Error(), "no node ACL present"))) || currentNodeACL == nil {
 			if _, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(nodes[i].Network), nodeacls.NodeID(nodes[i].ID), acls.Allowed); err != nil {