Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding security fixes #1500

Merged
merged 2 commits into from
Aug 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 1 addition & 16 deletions controllers/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 {
Expand All @@ -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
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand All @@ -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 {
Expand Down
83 changes: 42 additions & 41 deletions controllers/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package controller

import (
"encoding/json"
"errors"
"net/http"
"strings"

Expand All @@ -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")
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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, " ")
Expand All @@ -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"]
Expand Down
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down
5 changes: 5 additions & 0 deletions models/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package models

type Error string

func (e Error) Error() string { return string(e) }
14 changes: 12 additions & 2 deletions serverctl/serverctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down