mirror of
https://github.com/luscis/openlan.git
synced 2025-10-06 17:17:00 +08:00
fea: support add or del acl rule
This commit is contained in:
@@ -2,6 +2,7 @@ package v5
|
||||
|
||||
import (
|
||||
"github.com/luscis/openlan/cmd/api"
|
||||
"github.com/luscis/openlan/pkg/schema"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@@ -9,30 +10,6 @@ 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
pkg/api/acl.go
Executable file
87
pkg/api/acl.go
Executable file
@@ -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
|
||||
}
|
||||
}
|
@@ -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)
|
||||
|
@@ -24,4 +24,5 @@ func Add(router *mux.Router, switcher Switcher) {
|
||||
OpenAPI{}.Router(router)
|
||||
ZTrust{}.Router(router)
|
||||
Output{}.Router(router)
|
||||
ACL{}.Router(router)
|
||||
}
|
||||
|
139
pkg/switch/acl.go
Normal file
139
pkg/switch/acl.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
@@ -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{
|
||||
w.fire.Raw.Pre.AddRule(cn.IPRule{
|
||||
Input: input,
|
||||
Jump: acl,
|
||||
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
|
||||
}
|
||||
|
@@ -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",
|
||||
|
Reference in New Issue
Block a user