Skip to content

Commit

Permalink
Merge pull request #3172 from gravitl/NET-1615
Browse files Browse the repository at this point in the history
NET-1615: ACLS v2
  • Loading branch information
abhishek9686 authored Oct 31, 2024
2 parents dd367be + df3662f commit 2d3d5fe
Show file tree
Hide file tree
Showing 37 changed files with 1,641 additions and 191 deletions.
230 changes: 230 additions & 0 deletions controllers/acls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
package controller

import (
"encoding/json"
"errors"
"net/http"
"net/url"
"time"

"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
)

func aclHandlers(r *mux.Router) {
r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(getAcls))).
Methods(http.MethodGet)
r.HandleFunc("/api/v1/acls/policy_types", logic.SecurityCheck(true, http.HandlerFunc(aclPolicyTypes))).
Methods(http.MethodGet)
r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(createAcl))).
Methods(http.MethodPost)
r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(updateAcl))).
Methods(http.MethodPut)
r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(deleteAcl))).
Methods(http.MethodDelete)
r.HandleFunc("/api/v1/acls/debug", logic.SecurityCheck(true, http.HandlerFunc(aclDebug))).
Methods(http.MethodGet)
}

// @Summary List Acl Policy types
// @Router /api/v1/acls/policy_types [get]
// @Tags ACL
// @Accept json
// @Success 200 {array} models.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func aclPolicyTypes(w http.ResponseWriter, r *http.Request) {
resp := models.AclPolicyTypes{
RuleTypes: []models.AclPolicyType{
models.DevicePolicy,
models.UserPolicy,
},
SrcGroupTypes: []models.AclGroupType{
models.UserAclID,
models.UserGroupAclID,
models.DeviceAclID,
},
DstGroupTypes: []models.AclGroupType{
models.DeviceAclID,
// models.NetmakerIPAclID,
// models.NetmakerSubNetRangeAClID,
},
}
logic.ReturnSuccessResponseWithJson(w, r, resp, "fetched acls types")
}

func aclDebug(w http.ResponseWriter, r *http.Request) {
nodeID, _ := url.QueryUnescape(r.URL.Query().Get("node"))
peerID, _ := url.QueryUnescape(r.URL.Query().Get("peer"))
node, err := logic.GetNodeByID(nodeID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
peer, err := logic.GetNodeByID(peerID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
allowed := logic.IsNodeAllowedToCommunicate(node, peer)
logic.ReturnSuccessResponseWithJson(w, r, allowed, "fetched all acls in the network ")
}

// @Summary List Acls in a network
// @Router /api/v1/acls [get]
// @Tags ACL
// @Accept json
// @Success 200 {array} models.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func getAcls(w http.ResponseWriter, r *http.Request) {
netID := r.URL.Query().Get("network")
if netID == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network id param is missing"), "badrequest"))
return
}
// check if network exists
_, err := logic.GetNetwork(netID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
acls, err := logic.ListAcls(models.NetworkID(netID))
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to get all network acl entries: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
logic.SortAclEntrys(acls[:])
logic.ReturnSuccessResponseWithJson(w, r, acls, "fetched all acls in the network "+netID)
}

// @Summary Create Acl
// @Router /api/v1/acls [post]
// @Tags ACL
// @Accept json
// @Success 200 {array} models.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func createAcl(w http.ResponseWriter, r *http.Request) {
var req models.Acl
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
logger.Log(0, "error decoding request body: ",
err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
user, err := logic.GetUser(r.Header.Get("user"))
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
err = logic.ValidateCreateAclReq(req)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}

acl := req
acl.ID = uuid.New().String()
acl.CreatedBy = user.UserName
acl.CreatedAt = time.Now().UTC()
acl.Default = false
if acl.RuleType == models.DevicePolicy {
acl.AllowedDirection = models.TrafficDirectionBi
} else {
acl.AllowedDirection = models.TrafficDirectionUni
}
// validate create acl policy
if !logic.IsAclPolicyValid(acl) {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid policy"), "badrequest"))
return
}
err = logic.InsertAcl(acl)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
acl, err = logic.GetAcl(acl.ID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
go mq.PublishPeerUpdate(false)
logic.ReturnSuccessResponseWithJson(w, r, acl, "created acl successfully")
}

// @Summary Update Acl
// @Router /api/v1/acls [put]
// @Tags ACL
// @Accept json
// @Success 200 {array} models.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func updateAcl(w http.ResponseWriter, r *http.Request) {
var updateAcl models.UpdateAclRequest
err := json.NewDecoder(r.Body).Decode(&updateAcl)
if err != nil {
logger.Log(0, "error decoding request body: ",
err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}

acl, err := logic.GetAcl(updateAcl.ID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
if !logic.IsAclPolicyValid(updateAcl.Acl) {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid policy"), "badrequest"))
return
}
if updateAcl.Acl.NetworkID != acl.NetworkID {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid policy, network id mismatch"), "badrequest"))
return
}
if !acl.Default && updateAcl.NewName != "" {
//check if policy exists with same name
updateAcl.Acl.Name = updateAcl.NewName
}
err = logic.UpdateAcl(updateAcl.Acl, acl)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
go mq.PublishPeerUpdate(false)
logic.ReturnSuccessResponse(w, r, "updated acl "+acl.Name)
}

// @Summary Delete Acl
// @Router /api/v1/acls [delete]
// @Tags ACL
// @Accept json
// @Success 200 {array} models.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func deleteAcl(w http.ResponseWriter, r *http.Request) {
aclID, _ := url.QueryUnescape(r.URL.Query().Get("acl_id"))
if aclID == "" {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("acl id is required"), "badrequest"))
return
}
acl, err := logic.GetAcl(aclID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
if acl.Default {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot delete default policy"), "badrequest"))
return
}
err = logic.DeleteAcl(acl)
if err != nil {
logic.ReturnErrorResponse(w, r,
logic.FormatError(errors.New("cannot delete default policy"), "internal"))
return
}
go mq.PublishPeerUpdate(false)
logic.ReturnSuccessResponse(w, r, "deleted acl "+acl.Name)
}
1 change: 1 addition & 0 deletions controllers/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var HttpHandlers = []interface{}{
hostHandlers,
enrollmentKeyHandlers,
tagHandlers,
aclHandlers,
legacyHandlers,
}

Expand Down
3 changes: 2 additions & 1 deletion controllers/enrollmentkeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) {
func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
keyID := params["keyID"]
err := logic.DeleteEnrollmentKey(keyID)
err := logic.DeleteEnrollmentKey(keyID, false)
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to remove enrollment key: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
Expand Down Expand Up @@ -159,6 +159,7 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
enrollmentKeyBody.Groups,
enrollmentKeyBody.Unlimited,
relayId,
false,
)
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to create enrollment key:", err.Error())
Expand Down
7 changes: 4 additions & 3 deletions controllers/ext_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,13 +468,14 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
extclient.OwnerID = userName
extclient.RemoteAccessClientID = customExtClient.RemoteAccessClientID
extclient.IngressGatewayID = nodeid

extclient.Network = node.Network
extclient.Tags = make(map[models.TagID]struct{})
extclient.Tags[models.TagID(fmt.Sprintf("%s.%s", extclient.Network,
models.RemoteAccessTagName))] = struct{}{}
// set extclient dns to ingressdns if extclient dns is not explicitly set
if (extclient.DNS == "") && (node.IngressDNS != "") {
extclient.DNS = node.IngressDNS
}

extclient.Network = node.Network
host, err := logic.GetHost(node.HostID.String())
if err != nil {
logger.Log(0, r.Header.Get("user"),
Expand Down
13 changes: 0 additions & 13 deletions controllers/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,19 +253,6 @@ func updateHost(w http.ResponseWriter, r *http.Request) {

newHost := newHostData.ConvertAPIHostToNMHost(currHost)

if newHost.Name != currHost.Name {
// update any rag role ids
for _, nodeID := range newHost.Nodes {
node, err := logic.GetNodeByID(nodeID)
if err == nil && node.IsIngressGateway {
role, err := logic.GetRole(models.GetRAGRoleID(node.Network, currHost.ID.String()))
if err == nil {
role.UiName = models.GetRAGRoleName(node.Network, newHost.Name)
logic.UpdateRole(role)
}
}
}
}
logic.UpdateHost(newHost, currHost) // update the in memory struct values
if err = logic.UpsertHost(newHost); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to update a host:", err.Error())
Expand Down
7 changes: 6 additions & 1 deletion controllers/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ func userMiddleWare(handler http.Handler) http.Handler {
r.Header.Set("TARGET_RSRC_ID", "")
r.Header.Set("RAC", "")
r.Header.Set("NET_ID", params["network"])
if r.URL.Query().Get("network") != "" {
r.Header.Set("NET_ID", r.URL.Query().Get("network"))
}
if strings.Contains(route, "hosts") || strings.Contains(route, "nodes") {
r.Header.Set("TARGET_RSRC", models.HostRsrc.String())
}
Expand Down Expand Up @@ -57,6 +60,9 @@ func userMiddleWare(handler http.Handler) http.Handler {
if strings.Contains(route, "acls") {
r.Header.Set("TARGET_RSRC", models.AclRsrc.String())
}
if strings.Contains(route, "tags") {
r.Header.Set("TARGET_RSRC", models.TagRsrc.String())
}
if strings.Contains(route, "extclients") {
r.Header.Set("TARGET_RSRC", models.ExtClientsRsrc.String())
}
Expand Down Expand Up @@ -105,7 +111,6 @@ func userMiddleWare(handler http.Handler) http.Handler {
r.Header.Get("TARGET_RSRC") == models.UserRsrc.String()) {
r.Header.Set("IS_GLOBAL_ACCESS", "yes")
}

r.Header.Set("RSRC_TYPE", r.Header.Get("TARGET_RSRC"))
handler.ServeHTTP(w, r)
})
Expand Down
3 changes: 2 additions & 1 deletion controllers/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
return
}
go logic.DeleteNetworkRoles(network)
go logic.DeleteDefaultNetworkPolicies(models.NetworkID(network))
//delete network from allocated ip map
go logic.RemoveNetworkFromAllocatedIpMap(network)

Expand Down Expand Up @@ -530,8 +531,8 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}

logic.CreateDefaultNetworkRolesAndGroups(models.NetworkID(network.NetID))
logic.CreateDefaultAclNetworkPolicies(models.NetworkID(network.NetID))
logic.CreateDefaultTags(models.NetworkID(network.NetID))
//add new network to allocated ip map
go logic.AddNetworkToAllocatedIpMap(network.NetID)
Expand Down
3 changes: 3 additions & 0 deletions controllers/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
if err := mq.NodeUpdate(&node); err != nil {
slog.Error("error publishing node update to node", "node", node.ID, "error", err)
}
mq.PublishPeerUpdate(false)
}()
}

Expand Down Expand Up @@ -634,6 +635,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
if err := mq.PublishSingleHostPeerUpdate(host, allNodes, nil, removedClients[:], false, nil); err != nil {
slog.Error("publishSingleHostUpdate", "host", host.Name, "error", err)
}
mq.PublishPeerUpdate(false)
if err := mq.NodeUpdate(&node); err != nil {
slog.Error(
"error publishing node update to node",
Expand Down Expand Up @@ -749,6 +751,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
logger.Log(0, "error during node ACL update for node", newNode.ID.String())
}
}
mq.PublishPeerUpdate(false)
if servercfg.IsDNSMode() {
logic.SetDNS()
}
Expand Down
Loading

0 comments on commit 2d3d5fe

Please sign in to comment.