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

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
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.