Skip to content

Commit

Permalink
fea: support add or del acl rule
Browse files Browse the repository at this point in the history
danieldin95 committed Mar 27, 2024
1 parent 7187561 commit 34c452c
Showing 7 changed files with 322 additions and 84 deletions.
133 changes: 67 additions & 66 deletions cmd/api/v5/acl.go
Original file line number Diff line number Diff line change
@@ -2,37 +2,14 @@ package v5

import (
"github.com/luscis/openlan/cmd/api"
"github.com/luscis/openlan/pkg/schema"
"github.com/urfave/cli/v2"
)

type ACL struct {
Cmd
}

func (u ACL) Url(prefix, name string) string {
if name == "" {
return prefix + "/api/acl"
} else {
return prefix + "/api/acl/" + name
}
}

func (u ACL) Add(c *cli.Context) error {
return nil
}

func (u ACL) Remove(c *cli.Context) error {
return nil
}

func (u ACL) List(c *cli.Context) error {
return nil
}

func (u ACL) Apply(c *cli.Context) error {
return nil
}

func (u ACL) Commands(app *api.App) {
rule := ACLRule{}
app.Command(&cli.Command{
@@ -42,32 +19,7 @@ func (u ACL) Commands(app *api.App) {
&cli.StringFlag{Name: "name", Aliases: []string{"n"}},
},
Subcommands: []*cli.Command{
{
Name: "add",
Usage: "Add a new acl",
Action: u.Add,
},
{
Name: "remove",
Usage: "Remove an existing acl",
Aliases: []string{"ls"},
Action: u.Remove,
},
{
Name: "list",
Usage: "Display all acl",
Aliases: []string{"ls"},
Action: u.List,
},
rule.Commands(),
{
Name: "apply",
Usage: "Apply a new acl",
Flags: []cli.Flag{
&cli.StringFlag{Name: "network", Aliases: []string{"net"}},
},
Action: u.Apply,
},
},
})
}
@@ -76,24 +28,73 @@ type ACLRule struct {
Cmd
}

func (u ACLRule) Url(prefix, acl, name string) string {
if name == "" {
return prefix + "/api/acl/" + acl
} else {
return prefix + "/api/acl/" + acl + "/" + name
}
func (u ACLRule) Url(prefix, name string) string {
return prefix + "/api/network/" + name + "/acl"
}

func (u ACLRule) Add(c *cli.Context) error {
name := c.String("name")
url := u.Url(c.String("url"), name)

rule := &schema.ACLRule{
Proto: c.String("protocol"),
SrcIp: c.String("source"),
DstIp: c.String("destination"),
SrcPort: c.Int("sport"),
DstPort: c.Int("dport"),
Action: "DROP",
}

clt := u.NewHttp(c.String("token"))
if err := clt.PostJSON(url, rule, nil); err != nil {
return err
}

return nil
}

func (u ACLRule) Remove(c *cli.Context) error {
name := c.String("name")
url := u.Url(c.String("url"), name)

rule := &schema.ACLRule{
Proto: c.String("protocol"),
SrcIp: c.String("source"),
DstIp: c.String("destination"),
SrcPort: c.Int("sport"),
DstPort: c.Int("dport"),
Action: "DROP",
}

clt := u.NewHttp(c.String("token"))
if err := clt.DeleteJSON(url, rule, nil); err != nil {
return err
}

return nil
}

func (u ACLRule) Tmpl() string {
return `# total {{ len . }}
{{ps -15 "source"}} {{ps -15 "destination"}} {{ps -4 "protocol"}} {{ps -4 "dport"}} {{ps -4 "sport"}}
{{- range . }}
{{ps -15 .SrcIp}} {{ps -15 .DstIp}} {{ps -4 .Proto}} {{pi -4 .DstPort}} {{pi -4 .SrcPort}}
{{- end }}
`
}

func (u ACLRule) List(c *cli.Context) error {
return nil
name := c.String("name")

url := u.Url(c.String("url"), name)
clt := u.NewHttp(c.String("token"))

var items []schema.ACLRule
if err := clt.GetJSON(url, &items); err != nil {
return err
}

return u.Out(items, c.String("format"), u.Tmpl())
}

func (u ACLRule) Commands() *cli.Command {
@@ -105,11 +106,11 @@ func (u ACLRule) Commands() *cli.Command {
Name: "add",
Usage: "Add a new acl rule",
Flags: []cli.Flag{
&cli.StringFlag{Name: "src", Aliases: []string{"s"}},
&cli.StringFlag{Name: "dst", Aliases: []string{"d"}},
&cli.StringFlag{Name: "proto", Aliases: []string{"p"}},
&cli.StringFlag{Name: "sport", Aliases: []string{"dp"}},
&cli.StringFlag{Name: "dport", Aliases: []string{"sp"}},
&cli.StringFlag{Name: "source", Aliases: []string{"s"}},
&cli.StringFlag{Name: "destination", Aliases: []string{"d"}},
&cli.StringFlag{Name: "protocol", Aliases: []string{"p"}},
&cli.IntFlag{Name: "sport", Aliases: []string{"dp"}},
&cli.IntFlag{Name: "dport", Aliases: []string{"sp"}},
},
Action: u.Add,
},
@@ -118,11 +119,11 @@ func (u ACLRule) Commands() *cli.Command {
Usage: "remove a new acl rule",
Aliases: []string{"rm"},
Flags: []cli.Flag{
&cli.StringFlag{Name: "src", Aliases: []string{"s"}},
&cli.StringFlag{Name: "dst", Aliases: []string{"d"}},
&cli.StringFlag{Name: "proto", Aliases: []string{"p"}},
&cli.StringFlag{Name: "sport", Aliases: []string{"dp"}},
&cli.StringFlag{Name: "dport", Aliases: []string{"sp"}},
&cli.StringFlag{Name: "source", Aliases: []string{"s"}},
&cli.StringFlag{Name: "destination", Aliases: []string{"d"}},
&cli.StringFlag{Name: "protocol", Aliases: []string{"p"}},
&cli.IntFlag{Name: "sport", Aliases: []string{"dp"}},
&cli.IntFlag{Name: "dport", Aliases: []string{"sp"}},
},
Action: u.Remove,
},
87 changes: 87 additions & 0 deletions pkg/api/acl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package api

import (
"net/http"

"github.com/gorilla/mux"
"github.com/luscis/openlan/pkg/schema"
)

type ACL struct {
Switcher Switcher
}

func (h ACL) Router(router *mux.Router) {
router.HandleFunc("/api/network/{id}/acl", h.List).Methods("GET")
router.HandleFunc("/api/network/{id}/acl", h.Add).Methods("POST")
router.HandleFunc("/api/network/{id}/acl", h.Del).Methods("DELETE")
}

func (h ACL) List(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]

worker := GetWorker(id)
if worker == nil {
http.Error(w, "Network not found", http.StatusInternalServerError)
return
}
acl := worker.ACLer()

rules := make([]schema.ACLRule, 0, 1024)
acl.ListRules(func(obj schema.ACLRule) {
rules = append(rules, obj)
})

ResponseJson(w, rules)
}

func (h ACL) Add(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]

worker := GetWorker(id)
if worker == nil {
http.Error(w, "Network not found", http.StatusInternalServerError)
return
}
acl := worker.ACLer()

rule := &schema.ACLRule{}
if err := GetData(r, rule); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

if err := acl.AddRule(rule); err == nil {
ResponseJson(w, "success")
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}

func (h ACL) Del(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]

worker := GetWorker(id)
if worker == nil {
http.Error(w, "Network not found", http.StatusInternalServerError)
return
}
acl := worker.ACLer()

rule := &schema.ACLRule{}
if err := GetData(r, rule); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

if err := acl.DelRule(rule); err == nil {
ResponseJson(w, "success")
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
7 changes: 7 additions & 0 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
@@ -32,6 +32,12 @@ func NewWorkerSchema(s Switcher) schema.Worker {
}
}

type ACLer interface {
AddRule(rule *schema.ACLRule) error
DelRule(rule *schema.ACLRule) error
ListRules(call func(obj schema.ACLRule))
}

type ZTruster interface {
AddGuest(name, source string) error
DelGuest(name, source string) error
@@ -53,6 +59,7 @@ type Networker interface {
Provider() string
ZTruster() ZTruster
IfAddr() string
ACLer() ACLer
}

var workers = make(map[string]Networker)
1 change: 1 addition & 0 deletions pkg/api/url.go
Original file line number Diff line number Diff line change
@@ -24,4 +24,5 @@ func Add(router *mux.Router, switcher Switcher) {
OpenAPI{}.Router(router)
ZTrust{}.Router(router)
Output{}.Router(router)
ACL{}.Router(router)
}
139 changes: 139 additions & 0 deletions pkg/switch/acl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package cswitch

import (
"fmt"
"strconv"

"github.com/luscis/openlan/pkg/libol"
cn "github.com/luscis/openlan/pkg/network"
"github.com/luscis/openlan/pkg/schema"
)

type ACLRule struct {
SrcIp string
DstIp string
Proto string // TCP, UDP or ICMP
SrcPort int
DstPort int
Action string // DROP or ACCEPT
rule *cn.IPRule
}

func (r *ACLRule) Id() string {
return fmt.Sprintf("%s:%s:%s:%d:%d", r.SrcIp, r.DstIp, r.Proto, r.DstPort, r.SrcPort)
}

func (r *ACLRule) Rule() cn.IPRule {
if r.rule == nil {
r.rule = &cn.IPRule{
Dest: r.DstIp,
Source: r.SrcIp,
Proto: r.Proto,
Jump: r.Action,
}
if r.DstPort > 0 {
r.rule.DstPort = strconv.Itoa(r.DstPort)
}
if r.SrcPort > 0 {
r.rule.SrcPort = strconv.Itoa(r.SrcPort)
}
}
return *r.rule
}

type ACL struct {
Name string
Rules map[string]*ACLRule
chain *cn.FireWallChain
out *libol.SubLogger
}

func NewACL(name string) *ACL {
return &ACL{
Name: name,
out: libol.NewSubLogger(name),
Rules: make(map[string]*ACLRule, 32),
}
}

func (a *ACL) Chain() string {
return "ATT_" + a.Name
}

func (a *ACL) Initialize() {
a.chain = cn.NewFireWallChain(a.Chain(), cn.TRaw, "")
}

func (a *ACL) Start() {
a.out.Info("ACL.Start")
a.chain.Install()
}

func (a *ACL) Stop() {
a.out.Info("ACL.Stop")
a.chain.Cancel()
}

func (a *ACL) AddRule(rule *schema.ACLRule) error {
ar := &ACLRule{
Proto: rule.Proto,
DstIp: rule.DstIp,
SrcIp: rule.SrcIp,
DstPort: rule.DstPort,
SrcPort: rule.SrcPort,
Action: rule.Action,
}

a.out.Info("ACL.AddRule %s", ar.Id())

if _, ok := a.Rules[ar.Id()]; ok {
return libol.NewErr("AddRule: already existed")
}

if err := a.chain.AddRuleX(ar.Rule()); err == nil {
a.Rules[ar.Id()] = ar
} else {
a.out.Warn("ACL.AddRule %s", err)
}

return nil
}

func (a *ACL) DelRule(rule *schema.ACLRule) error {
ar := &ACLRule{
Proto: rule.Proto,
DstIp: rule.DstIp,
SrcIp: rule.SrcIp,
DstPort: rule.DstPort,
SrcPort: rule.SrcPort,
Action: rule.Action,
}

a.out.Info("ACL.DelRule %s", ar.Id())

if _, ok := a.Rules[ar.Id()]; !ok {
return nil
}

if err := a.chain.DelRuleX(ar.Rule()); err == nil {
delete(a.Rules, ar.Id())
} else {
a.out.Warn("ACL.DelRule %s", err)
}

return nil
}

func (a *ACL) ListRules(call func(obj schema.ACLRule)) {
for _, rule := range a.Rules {
obj := schema.ACLRule{
SrcIp: rule.SrcIp,
DstIp: rule.DstIp,
SrcPort: rule.SrcPort,
DstPort: rule.DstPort,
Proto: rule.Proto,
Action: rule.Action,
}
call(obj)
}
}
34 changes: 19 additions & 15 deletions pkg/switch/network.go
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@ type WorkerImpl struct {
vrf *cn.VRF
table int
br cn.Bridger
acl *ACL
}

func NewWorkerApi(c *co.Network) *WorkerImpl {
@@ -80,6 +81,9 @@ func (w *WorkerImpl) Initialize() {
w.table = w.vrf.Table()
}

w.acl = NewACL(cfg.Name)
w.acl.Initialize()

n := models.Network{
Name: cfg.Name,
IpStart: cfg.Subnet.Start,
@@ -313,12 +317,8 @@ func (w *WorkerImpl) Start(v api.Switcher) {
w.loadVRF()
w.loadRoutes()

if cfg.Acl != "" {
fire.Mangle.Pre.AddRule(cn.IPRule{
Input: cfg.Bridge.Name,
Jump: cfg.Acl,
})
}
w.acl.Start()
w.toACL(cfg.Bridge.Name)

if cfg.Bridge.Mss > 0 {
// forward to remote
@@ -498,6 +498,8 @@ func (w *WorkerImpl) Stop() {
}
w.outputs = nil

w.acl.Stop()

w.setR.Destroy()
w.setV.Destroy()
}
@@ -546,16 +548,14 @@ func (w *WorkerImpl) Subnet() *net.IPNet {
func (w *WorkerImpl) Reload(v api.Switcher) {
}

func (w *WorkerImpl) toACL(acl, input string) {
func (w *WorkerImpl) toACL(input string) {
if input == "" {
return
}
if acl != "" {
w.fire.Mangle.Pre.AddRule(cn.IPRule{
Input: input,
Jump: acl,
})
}
w.fire.Raw.Pre.AddRule(cn.IPRule{
Input: input,
Jump: w.acl.Chain(),
})
}

func (w *WorkerImpl) openPort(protocol, port, comment string) {
@@ -689,7 +689,7 @@ func (w *WorkerImpl) forwardZone(input string) {
}

func (w *WorkerImpl) forwardVPN() {
cfg, vpn := w.GetCfgs()
_, vpn := w.GetCfgs()
if vpn == nil {
return
}
@@ -706,7 +706,7 @@ func (w *WorkerImpl) forwardVPN() {

// Enable MASQUERADE, and FORWARD it.
w.toRelated(devName, "Accept related")
w.toACL(cfg.Acl, devName)
w.toACL(devName)

for _, rt := range vpn.Routes {
if rt == "0.0.0.0/0" {
@@ -784,3 +784,7 @@ func (w *WorkerImpl) ZTruster() api.ZTruster {
func (w *WorkerImpl) IfAddr() string {
return strings.SplitN(w.cfg.Bridge.Address, "/", 2)[0]
}

func (w *WorkerImpl) ACLer() api.ACLer {
return w.acl
}
5 changes: 2 additions & 3 deletions pkg/switch/ztrust.go
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@ import (
"time"

"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/network"
cn "github.com/luscis/openlan/pkg/network"
"github.com/luscis/openlan/pkg/schema"
)
@@ -69,7 +68,7 @@ func (g *ZGuest) Chain() string {
}

func (g *ZGuest) Start() {
g.chain = cn.NewFireWallChain(g.Chain(), network.TMangle, "")
g.chain = cn.NewFireWallChain(g.Chain(), cn.TMangle, "")
g.chain.Install()
}

@@ -168,7 +167,7 @@ func (z *ZTrust) Chain() string {
}

func (z *ZTrust) Initialize() {
z.chain = cn.NewFireWallChain(z.Chain(), network.TMangle, "")
z.chain = cn.NewFireWallChain(z.Chain(), cn.TMangle, "")
z.chain.AddRule(cn.IPRule{
Comment: "ZTrust Deny All",
Jump: "DROP",

0 comments on commit 34c452c

Please sign in to comment.