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

NET-1604: New Simplified RAC Apis #3147

Merged
merged 10 commits into from
Oct 1, 2024
4 changes: 4 additions & 0 deletions controllers/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ func userMiddleWare(handler http.Handler) http.Handler {
r.Header.Set("TARGET_RSRC", "")
r.Header.Set("RSRC_TYPE", "")
r.Header.Set("TARGET_RSRC_ID", "")
r.Header.Set("RAC", "")
r.Header.Set("NET_ID", params["network"])
if strings.Contains(route, "hosts") || strings.Contains(route, "nodes") {
r.Header.Set("TARGET_RSRC", models.HostRsrc.String())
}
if strings.Contains(route, "dns") {
r.Header.Set("TARGET_RSRC", models.DnsRsrc.String())
}
if strings.Contains(route, "rac") {
r.Header.Set("RAC", "true")
}
if strings.Contains(route, "users") {
r.Header.Set("TARGET_RSRC", models.UserRsrc.String())
}
Expand Down
10 changes: 10 additions & 0 deletions models/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ type UserRemoteGws struct {
NetworkAddresses []string `json:"network_addresses"`
}

// UserRAGs - struct for user access gws
type UserRAGs struct {
GwID string `json:"remote_access_gw_id"`
GWName string `json:"gw_name"`
Network string `json:"network"`
Connected bool `json:"connected"`
IsInternetGateway bool `json:"is_internet_gateway"`
Metadata string `json:"metadata"`
}

// UserRemoteGwsReq - struct to hold user remote acccess gws req
type UserRemoteGwsReq struct {
RemoteAccessClientID string `json:"remote_access_clientid"`
Expand Down
14 changes: 14 additions & 0 deletions pro/controllers/rac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package controllers

import (
"net/http"

"github.com/gorilla/mux"
"github.com/gravitl/netmaker/logic"
)

func RacHandlers(r *mux.Router) {
r.HandleFunc("/api/v1/rac/networks", logic.SecurityCheck(false, http.HandlerFunc(getUserRemoteAccessNetworks))).Methods(http.MethodGet)
r.HandleFunc("/api/v1/rac/network/{network}/access_points", logic.SecurityCheck(false, http.HandlerFunc(getUserRemoteAccessNetworkGateways))).Methods(http.MethodGet)
r.HandleFunc("/api/v1/rac/access_point/{access_point_id}/config", logic.SecurityCheck(false, http.HandlerFunc(getRemoteAccessGatewayConf))).Methods(http.MethodGet)
}
214 changes: 213 additions & 1 deletion pro/controllers/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,6 @@ func inviteUsers(w http.ResponseWriter, r *http.Request) {
}(invite)
}
logic.ReturnSuccessResponse(w, r, "triggered user invites")

}

// swagger:route GET /api/v1/users/invites user listUserInvites
Expand Down Expand Up @@ -816,6 +815,218 @@ func removeUserFromRemoteAccessGW(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
}

// @Summary Get Users Remote Access Gw Networks.
// @Router /api/users/{username}/remote_access_gw [get]
// @Tags Users
// @Param username path string true "Username to fetch all the gateways with access"
// @Success 200 {object} map[string][]models.UserRemoteGws
// @Failure 500 {object} models.ErrorResponse
func getUserRemoteAccessNetworks(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
username := r.Header.Get("user")
user, err := logic.GetUser(username)
if err != nil {
logger.Log(0, username, "failed to fetch user: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest"))
return
}
userGws := make(map[string][]models.UserRemoteGws)
networks := []models.Network{}
networkMap := make(map[string]struct{})
userGwNodes := proLogic.GetUserRAGNodes(*user)
for _, node := range userGwNodes {
network, err := logic.GetNetwork(node.Network)
if err != nil {
slog.Error("failed to get node network", "error", err)
continue
}
if _, ok := networkMap[network.NetID]; ok {
continue
}
networkMap[network.NetID] = struct{}{}
networks = append(networks, network)
}

slog.Debug("returned user gws", "user", username, "gws", userGws)
logic.ReturnSuccessResponseWithJson(w, r, networks, "fetched user accessible networks")
}

// @Summary Get Users Remote Access Gw Networks.
// @Router /api/users/{username}/remote_access_gw [get]
// @Tags Users
// @Param username path string true "Username to fetch all the gateways with access"
// @Success 200 {object} map[string][]models.UserRemoteGws
// @Failure 500 {object} models.ErrorResponse
func getUserRemoteAccessNetworkGateways(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
username := r.Header.Get("user")
user, err := logic.GetUser(username)
if err != nil {
logger.Log(0, username, "failed to fetch user: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest"))
return
}
network := params["network"]
if network == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params network"), "badrequest"))
return
}
userGws := []models.UserRAGs{}

userGwNodes := proLogic.GetUserRAGNodes(*user)
for _, node := range userGwNodes {
if node.Network != network {
continue
}

host, err := logic.GetHost(node.HostID.String())
if err != nil {
continue
}

userGws = append(userGws, models.UserRAGs{
GwID: node.ID.String(),
GWName: host.Name,
Network: node.Network,
IsInternetGateway: node.IsInternetGateway,
Metadata: node.Metadata,
})

}

slog.Debug("returned user gws", "user", username, "gws", userGws)
logic.ReturnSuccessResponseWithJson(w, r, userGws, "fetched user accessible gateways in network "+network)
}

// @Summary Get Users Remote Access Gw Networks.
// @Router /api/users/{username}/remote_access_gw [get]
// @Tags Users
// @Param username path string true "Username to fetch all the gateways with access"
// @Success 200 {object} map[string][]models.UserRemoteGws
// @Failure 500 {object} models.ErrorResponse
func getRemoteAccessGatewayConf(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
username := r.Header.Get("user")
user, err := logic.GetUser(username)
if err != nil {
logger.Log(0, username, "failed to fetch user: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest"))
return
}
remoteGwID := params["access_point_id"]
if remoteGwID == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params access_point_id"), "badrequest"))
return
}
var req models.UserRemoteGwsReq
err = json.NewDecoder(r.Body).Decode(&req)
if err != nil {
slog.Error("error decoding request body: ", "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}

userGwNodes := proLogic.GetUserRAGNodes(*user)
if _, ok := userGwNodes[remoteGwID]; !ok {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access denied"), "forbidden"))
return
}
node, err := logic.GetNodeByID(remoteGwID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch gw node %s, error: %v", remoteGwID, err), "badrequest"))
return
}
host, err := logic.GetHost(node.HostID.String())
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch gw host %s, error: %v", remoteGwID, err), "badrequest"))
return
}
network, err := logic.GetNetwork(node.Network)
if err != nil {
slog.Error("failed to get node network", "error", err)
}
var userConf models.ExtClient
allextClients, err := logic.GetAllExtClients()
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
for _, extClient := range allextClients {
if extClient.Network != network.NetID || extClient.IngressGatewayID != node.ID.String() {
continue
}
if extClient.RemoteAccessClientID == req.RemoteAccessClientID && extClient.OwnerID == username {
userConf = extClient
userConf.AllowedIPs = logic.GetExtclientAllowedIPs(extClient)
}
}
if userConf.ClientID == "" {
// create a new conf
userConf.OwnerID = user.UserName
userConf.RemoteAccessClientID = req.RemoteAccessClientID
userConf.IngressGatewayID = node.ID.String()

// set extclient dns to ingressdns if extclient dns is not explicitly set
if (userConf.DNS == "") && (node.IngressDNS != "") {
userConf.DNS = node.IngressDNS
}

userConf.Network = node.Network
host, err := logic.GetHost(node.HostID.String())
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to get ingress gateway host for node [%s] info: %v", node.ID, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
listenPort := logic.GetPeerListenPort(host)
if host.EndpointIP.To4() == nil {
userConf.IngressGatewayEndpoint = fmt.Sprintf("[%s]:%d", host.EndpointIPv6.String(), listenPort)
} else {
userConf.IngressGatewayEndpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), listenPort)
}
userConf.Enabled = true
parentNetwork, err := logic.GetNetwork(node.Network)
if err == nil { // check if parent network default ACL is enabled (yes) or not (no)
userConf.Enabled = parentNetwork.DefaultACL == "yes"
}
if err = logic.CreateExtClient(&userConf); err != nil {
slog.Error(
"failed to create extclient",
"user",
r.Header.Get("user"),
"network",
node.Network,
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
}
userGw := models.UserRemoteGws{
GwID: node.ID.String(),
GWName: host.Name,
Network: node.Network,
GwClient: userConf,
Connected: true,
IsInternetGateway: node.IsInternetGateway,
GwPeerPublicKey: host.PublicKey.String(),
GwListenPort: logic.GetPeerListenPort(host),
Metadata: node.Metadata,
AllowedEndpoints: getAllowedRagEndpoints(&node, host),
NetworkAddresses: []string{network.AddressRange, network.AddressRange6},
}

slog.Debug("returned user gw config", "user", user.UserName, "gws", userGw)
logic.ReturnSuccessResponseWithJson(w, r, userGw, "fetched user config to gw "+remoteGwID)
}

// @Summary Get Users Remote Access Gw.
// @Router /api/users/{username}/remote_access_gw [get]
// @Tags Users
Expand Down Expand Up @@ -876,6 +1087,7 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
network, err := logic.GetNetwork(node.Network)
if err != nil {
slog.Error("failed to get node network", "error", err)
continue
}

gws := userGws[node.Network]
Expand Down
1 change: 1 addition & 0 deletions pro/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func InitPro() {
proControllers.UserHandlers,
proControllers.FailOverHandlers,
proControllers.InetHandlers,
proControllers.RacHandlers,
)
controller.ListRoles = proControllers.ListRoles
logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
Expand Down
3 changes: 3 additions & 0 deletions pro/logic/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ func NetworkPermissionsCheck(username string, r *http.Request) error {
if targetRsrc == "" {
return errors.New("target rsrc is missing")
}
if r.Header.Get("RAC") == "true" && r.Method == http.MethodGet {
return nil
}
if netID == "" {
return errors.New("network id is missing")
}
Expand Down
25 changes: 25 additions & 0 deletions pro/logic/user_mgmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,31 @@ func HasNetworkRsrcScope(permissionTemplate models.UserRolePermissionTemplate, n
_, ok = rsrcScope[rsrcID]
return ok
}

func DoesUserHaveAccessToRAGNode(user models.User, node models.Node) bool {
userGwAccessScope := GetUserNetworkRolesWithRemoteVPNAccess(user)
logger.Log(3, fmt.Sprintf("User Gw Access Scope: %+v", userGwAccessScope))
_, allNetAccess := userGwAccessScope["*"]
if node.IsIngressGateway && !node.PendingDelete {
if allNetAccess {
return true
} else {
gwRsrcMap := userGwAccessScope[models.NetworkID(node.Network)]
scope, ok := gwRsrcMap[models.AllRemoteAccessGwRsrcID]
if !ok {
if scope, ok = gwRsrcMap[models.RsrcID(node.ID.String())]; !ok {
return false
}
}
if scope.VPNaccess {
return true
}

}
}
return false
}

func GetUserRAGNodes(user models.User) (gws map[string]models.Node) {
gws = make(map[string]models.Node)
userGwAccessScope := GetUserNetworkRolesWithRemoteVPNAccess(user)
Expand Down
Loading