From 8fc9dac9695f954bec7358400822e17eb231c31b Mon Sep 17 00:00:00 2001 From: afeiszli Date: Wed, 31 Aug 2022 11:38:29 -0400 Subject: [PATCH] adding security fixes --- controllers/node.go | 17 +------- controllers/security.go | 90 ++++++++++++++++++++++------------------- main.go | 4 +- models/error.go | 5 +++ serverctl/serverctl.go | 14 ++++++- 5 files changed, 69 insertions(+), 61 deletions(-) create mode 100644 models/error.go diff --git a/controllers/node.go b/controllers/node.go index fda392ff..01b16c2e 100644 --- a/controllers/node.go +++ b/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 { diff --git a/controllers/security.go b/controllers/security.go index ecf1edb4..ab6c4c00 100644 --- a/controllers/security.go +++ b/controllers/security.go @@ -2,7 +2,6 @@ package controller import ( "encoding/json" - "errors" "net/http" "strings" @@ -14,14 +13,30 @@ 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 { + /* + 1. Check master token + 2. Check if admin + 3. Check if network admin + + + */ + 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 +45,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 +67,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 } - if len(userNetworks) == 0 { - userNetworks = append(userNetworks, NO_NETWORKS_PRESENT) + 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 + } + // 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 +103,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 +123,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"] diff --git a/main.go b/main.go index f79e259d..5b19dfed 100644 --- a/main.go +++ b/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" { diff --git a/models/error.go b/models/error.go new file mode 100644 index 00000000..6abb412d --- /dev/null +++ b/models/error.go @@ -0,0 +1,5 @@ +package models + +type Error string + +func (e Error) Error() string { return string(e) } diff --git a/serverctl/serverctl.go b/serverctl/serverctl.go index ce63f11b..8ffc76b9 100644 --- a/serverctl/serverctl.go +++ b/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 {