diff --git a/cmd/api/utils.go b/cmd/api/utils.go index e7debce..bd8994e 100755 --- a/cmd/api/utils.go +++ b/cmd/api/utils.go @@ -1,6 +1,9 @@ package api -import "os" +import ( + "os" + "strings" +) func GetEnv(key, value string) string { val := os.Getenv(key) @@ -9,3 +12,19 @@ func GetEnv(key, value string) string { } return val } + +func SplitName(name string) (string, string) { + values := strings.SplitN(name, "@", 2) + if len(values) == 2 { + return values[0], values[1] + } + return name, "" +} + +func SplitSocket(value string) (string, string) { + values := strings.SplitN(value, ":", 2) + if len(values) == 2 { + return values[0], values[1] + } + return value, "" +} diff --git a/cmd/api/v5/cmd.go b/cmd/api/v5/cmd.go index cbcd059..7aaa91f 100755 --- a/cmd/api/v5/cmd.go +++ b/cmd/api/v5/cmd.go @@ -44,4 +44,6 @@ func Commands(app *api.App) { Policy{}.Commands(app) Version{}.Commands(app) Log{}.Commands(app) + ZGuest{}.Commands(app) + Knock{}.Commands(app) } diff --git a/cmd/api/v5/knock.go b/cmd/api/v5/knock.go new file mode 100755 index 0000000..fcf2165 --- /dev/null +++ b/cmd/api/v5/knock.go @@ -0,0 +1,112 @@ +package v5 + +import ( + "strings" + + "github.com/luscis/openlan/cmd/api" + "github.com/luscis/openlan/pkg/libol" + "github.com/luscis/openlan/pkg/schema" + "github.com/urfave/cli/v2" +) + +type Knock struct { + Cmd +} + +func (u Knock) Url(prefix, name string) string { + name, network := api.SplitName(name) + return prefix + "/api/ztrust/" + network + "/guest/" + name + "/knock" +} + +func (u Knock) Add(c *cli.Context) error { + username := c.String("name") + if !strings.Contains(username, "@") { + return libol.NewErr("invalid username") + } + socket := c.String("socket") + knock := &schema.KnockRule{ + Protocl: c.String("protocol"), + } + knock.Name, knock.Network = api.SplitName(username) + knock.Dest, knock.Port = api.SplitSocket(socket) + + url := u.Url(c.String("url"), username) + clt := u.NewHttp(c.String("token")) + if err := clt.PostJSON(url, knock, nil); err != nil { + return err + } + return nil +} + +func (u Knock) Remove(c *cli.Context) error { + username := c.String("name") + if !strings.Contains(username, "@") { + return libol.NewErr("invalid username") + } + socket := c.String("socket") + knock := &schema.KnockRule{ + Protocl: c.String("protocol"), + } + knock.Name, knock.Network = api.SplitName(username) + knock.Dest, knock.Port = api.SplitSocket(socket) + + url := u.Url(c.String("url"), username) + clt := u.NewHttp(c.String("token")) + if err := clt.DeleteJSON(url, knock, nil); err != nil { + return err + } + return nil +} + +func (u Knock) Tmpl() string { + return `# total {{ len . }} +{{ps -24 "username"}} {{ps -24 "address"}} +{{- range . }} +{{p2 -24 "%s@%s" .Name .Network}} {{ps -24 .Address}} +{{- end }} +` +} + +func (u Knock) List(c *cli.Context) error { + return nil +} + +func (u Knock) Commands(app *api.App) { + app.Command(&cli.Command{ + Name: "knock", + Aliases: []string{"kn"}, + Usage: "Knock configuration", + Subcommands: []*cli.Command{ + { + Name: "add", + Usage: "Add a knock", + Flags: []cli.Flag{ + &cli.StringFlag{Name: "name"}, + &cli.StringFlag{Name: "protocol"}, + &cli.StringFlag{Name: "socket"}, + }, + Action: u.Add, + }, + { + Name: "remove", + Usage: "Remove an existing knock", + Aliases: []string{"rm"}, + Flags: []cli.Flag{ + &cli.StringFlag{Name: "name"}, + &cli.StringFlag{Name: "protocol"}, + &cli.StringFlag{Name: "socket"}, + }, + Action: u.Remove, + }, + { + Name: "list", + Usage: "Display all knock", + Aliases: []string{"ls"}, + Flags: []cli.Flag{ + &cli.StringFlag{Name: "network"}, + }, + Action: u.List, + }, + }, + }) +} diff --git a/cmd/api/v5/point.go b/cmd/api/v5/point.go index 96f91f5..a6ab744 100755 --- a/cmd/api/v5/point.go +++ b/cmd/api/v5/point.go @@ -51,7 +51,7 @@ func (u Point) Commands(app *api.App) { app.Command(&cli.Command{ Name: "point", Aliases: []string{"ap"}, - Usage: "Point connected to this", + Usage: "Point accessed to switch", Subcommands: []*cli.Command{ { Name: "list", diff --git a/cmd/api/v5/user.go b/cmd/api/v5/user.go index e613d40..784218a 100755 --- a/cmd/api/v5/user.go +++ b/cmd/api/v5/user.go @@ -26,22 +26,17 @@ func (u User) Url(prefix, name string) string { func (u User) Add(c *cli.Context) error { username := c.String("name") + if !strings.Contains(username, "@") { + return libol.NewErr("invalid username") + } user := &schema.User{ Name: username, Password: c.String("password"), Role: c.String("role"), Lease: c.String("lease"), } - if user.Name == "" { - return libol.NewErr("name is empty") - } - if !strings.Contains(username, "@") { - return libol.NewErr("name not contains network") - } - values := strings.SplitN(username, "@", 2) - user.Name = values[0] - user.Network = values[1] - url := u.Url(c.String("url"), user.Name) + user.Name, user.Network = api.SplitName(username) + url := u.Url(c.String("url"), username) clt := u.NewHttp(c.String("token")) if err := clt.PostJSON(url, user, nil); err != nil { return err diff --git a/cmd/api/v5/zguest.go b/cmd/api/v5/zguest.go new file mode 100755 index 0000000..14a13ce --- /dev/null +++ b/cmd/api/v5/zguest.go @@ -0,0 +1,110 @@ +package v5 + +import ( + "strings" + + "github.com/luscis/openlan/cmd/api" + "github.com/luscis/openlan/pkg/libol" + "github.com/luscis/openlan/pkg/schema" + "github.com/urfave/cli/v2" +) + +type ZGuest struct { + Cmd +} + +func (u ZGuest) Url(prefix, name string) string { + name, network := api.SplitName(name) + if name == "" { + return prefix + "/api/ztrust/" + network + "/guest" + } else { + return prefix + "/api/ztrust/" + network + "/guest/" + name + } +} + +func (u ZGuest) Add(c *cli.Context) error { + username := c.String("name") + if !strings.Contains(username, "@") { + return libol.NewErr("invalid username") + } + guest := &schema.ZGuest{ + Name: username, + Address: c.String("address"), + } + guest.Name, guest.Network = api.SplitName(username) + url := u.Url(c.String("url"), username) + clt := u.NewHttp(c.String("token")) + if err := clt.PostJSON(url, guest, nil); err != nil { + return err + } + return nil +} + +func (u ZGuest) Remove(c *cli.Context) error { + username := c.String("name") + if !strings.Contains(username, "@") { + return libol.NewErr("invalid username") + } + guest := &schema.ZGuest{ + Name: username, + Address: c.String("address"), + } + guest.Name, guest.Network = api.SplitName(username) + url := u.Url(c.String("url"), username) + clt := u.NewHttp(c.String("token")) + if err := clt.DeleteJSON(url, guest, nil); err != nil { + return err + } + return nil +} + +func (u ZGuest) Tmpl() string { + return `# total {{ len . }} +{{ps -24 "username"}} {{ps -24 "address"}} +{{- range . }} +{{p2 -24 "%s@%s" .Name .Network}} {{ps -24 .Address}} +{{- end }} +` +} + +func (u ZGuest) List(c *cli.Context) error { + return nil +} + +func (u ZGuest) Commands(app *api.App) { + app.Command(&cli.Command{ + Name: "zguest", + Aliases: []string{"zg"}, + Usage: "zGuest configuration", + Subcommands: []*cli.Command{ + { + Name: "add", + Usage: "Add a zGuest", + Flags: []cli.Flag{ + &cli.StringFlag{Name: "name"}, + &cli.StringFlag{Name: "address"}, + }, + Action: u.Add, + }, + { + Name: "remove", + Usage: "Remove an existing zGuest", + Aliases: []string{"rm"}, + Flags: []cli.Flag{ + &cli.StringFlag{Name: "name"}, + &cli.StringFlag{Name: "address"}, + }, + Action: u.Remove, + }, + { + Name: "list", + Usage: "Display all zGuests", + Aliases: []string{"ls"}, + Flags: []cli.Flag{ + &cli.StringFlag{Name: "network"}, + }, + Action: u.List, + }, + }, + }) +} diff --git a/pkg/api/link.go b/pkg/api/link.go index 900cc39..095f76a 100755 --- a/pkg/api/link.go +++ b/pkg/api/link.go @@ -1,12 +1,13 @@ package api import ( + "net/http" + "github.com/gorilla/mux" "github.com/luscis/openlan/pkg/cache" "github.com/luscis/openlan/pkg/libol" "github.com/luscis/openlan/pkg/models" "github.com/luscis/openlan/pkg/schema" - "net/http" ) type Link struct { @@ -31,7 +32,7 @@ func (h Link) List(w http.ResponseWriter, r *http.Request) { func (h Link) Get(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - libol.Info("GetPoint %s", vars["id"]) + libol.Info("Link.Get %s", vars["id"]) link := cache.Link.Get(vars["id"]) if link != nil { diff --git a/pkg/api/switcher.go b/pkg/api/switcher.go index 37976ba..2a8b59b 100755 --- a/pkg/api/switcher.go +++ b/pkg/api/switcher.go @@ -1,8 +1,9 @@ package api import ( - "github.com/luscis/openlan/pkg/config" + co "github.com/luscis/openlan/pkg/config" "github.com/luscis/openlan/pkg/libol" + cn "github.com/luscis/openlan/pkg/network" "github.com/luscis/openlan/pkg/schema" ) @@ -10,7 +11,7 @@ type Switcher interface { UUID() string UpTime() int64 Alias() string - Config() *config.Switch + Config() *co.Switch Server() libol.SocketServer Reload() Save() @@ -28,3 +29,39 @@ func NewWorkerSchema(s Switcher) schema.Worker { Protocol: protocol, } } + +type ZTruster interface { + AddGuest(name, source string) error + DelGuest(name, source string) error + Knock(name string, protocol, dest, port string, age int) error +} + +type Networker interface { + String() string + ID() string + Initialize() + Start(v Switcher) + Stop() + Bridge() cn.Bridger + Config() *co.Network + Subnet() string + Reload(v Switcher) + Provider() string + ZTruster() ZTruster +} + +var workers = make(map[string]Networker) + +func AddWorker(name string, obj Networker) { + workers[name] = obj +} + +func GetWorker(name string) Networker { + return workers[name] +} + +func ListWorker(call func(w Networker)) { + for _, worker := range workers { + call(worker) + } +} diff --git a/pkg/api/url.go b/pkg/api/url.go index ac3e960..1812144 100755 --- a/pkg/api/url.go +++ b/pkg/api/url.go @@ -22,4 +22,5 @@ func Add(router *mux.Router, switcher Switcher) { Version{}.Router(router) Log{}.Router(router) OpenAPI{}.Router(router) + ZTrust{}.Router(router) } diff --git a/pkg/api/ztrust.go b/pkg/api/ztrust.go new file mode 100755 index 0000000..b6dca98 --- /dev/null +++ b/pkg/api/ztrust.go @@ -0,0 +1,140 @@ +package api + +import ( + "net/http" + + "github.com/gorilla/mux" + "github.com/luscis/openlan/pkg/libol" + "github.com/luscis/openlan/pkg/schema" +) + +type ZTrust struct { + Switcher Switcher +} + +func (h ZTrust) Router(router *mux.Router) { + router.HandleFunc("/api/ztrust", h.List).Methods("GET") + router.HandleFunc("/api/ztrust/{id}", h.Get).Methods("GET") + router.HandleFunc("/api/ztrust/{id}/guest/{user}", h.GetGuest).Methods("GET") + router.HandleFunc("/api/ztrust/{id}/guest/{user}", h.AddGuest).Methods("POST") + router.HandleFunc("/api/ztrust/{id}/guest/{user}", h.DelGuest).Methods("DELETE") + router.HandleFunc("/api/ztrust/{id}/guest/{user}/knock", h.ListKnock).Methods("GET") + router.HandleFunc("/api/ztrust/{id}/guest/{user}/knock", h.AddKnock).Methods("POST") +} + +func (h ZTrust) List(w http.ResponseWriter, r *http.Request) { + ResponseJson(w, "TODO") +} + +func (h ZTrust) Get(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + libol.Info("ZTrust.GET %s", vars["id"]) + ResponseJson(w, "TODO") +} + +func (h ZTrust) GetGuest(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + libol.Info("ZTrust.AddGuest %s", vars["id"]) + ResponseJson(w, "TODO") +} + +func (h ZTrust) AddGuest(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 + } + ztrust := worker.ZTruster() + if ztrust == nil { + http.Error(w, "ZTrust disabled", http.StatusInternalServerError) + return + } + + guest := &schema.ZGuest{} + if err := GetData(r, guest); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + guest.Name = vars["user"] + libol.Info("ZTrust.AddGuest %s@%s", guest.Name, id) + + if err := ztrust.AddGuest(guest.Name, guest.Address); err == nil { + ResponseJson(w, "success") + } else { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (h ZTrust) DelGuest(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 + } + ztrust := worker.ZTruster() + if ztrust == nil { + http.Error(w, "ZTrust disabled", http.StatusInternalServerError) + return + } + + guest := &schema.ZGuest{} + if err := GetData(r, guest); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + guest.Name = vars["user"] + libol.Info("ZTrust.DelGuest %s@%s", guest.Name, id) + if err := ztrust.DelGuest(guest.Name, guest.Address); err == nil { + ResponseJson(w, "success") + } else { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (h ZTrust) ListKnock(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + libol.Info("ZTrust.ListKnock %s", vars["id"]) + ResponseJson(w, "TODO") +} + +func (h ZTrust) AddKnock(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 + } + ztrust := worker.ZTruster() + if ztrust == nil { + http.Error(w, "ZTrust disabled", http.StatusInternalServerError) + return + } + + rule := &schema.KnockRule{} + if err := GetData(r, rule); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + name := vars["user"] + libol.Info("ZTrust.AddKnock %s@%s", rule.Name, id) + + if err := ztrust.Knock(name, rule.Protocl, rule.Dest, rule.Port, 0); err == nil { + ResponseJson(w, "success") + } else { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} diff --git a/pkg/config/network.go b/pkg/config/network.go index 83bea65..e5d86ed 100755 --- a/pkg/config/network.go +++ b/pkg/config/network.go @@ -23,7 +23,7 @@ type Network struct { Specifies interface{} `json:"specifies,omitempty"` Dhcp string `json:"dhcp,omitempty"` Outputs []Output `json:"outputs"` - ZeroTrust string `json:"zerotrust"` + ZTrust string `json:"ztrust"` } func (n *Network) NewSpecifies() interface{} { diff --git a/pkg/schema/ztrust.go b/pkg/schema/ztrust.go new file mode 100755 index 0000000..367d6a3 --- /dev/null +++ b/pkg/schema/ztrust.go @@ -0,0 +1,17 @@ +package schema + +type ZGuest struct { + Network string `json:"network"` + Name string `json:"name"` + Device string `json:"device"` + Address string `json:"Address"` +} + +type KnockRule struct { + Network string `json:"network"` + Name string `json:"name"` + Dest string `json:"destination"` + Protocl string `json:"protocol"` + Port string `json:"port"` + Age int `json:"age"` +} diff --git a/pkg/switch/confd.go b/pkg/switch/confd.go index 67f6b2a..6095427 100755 --- a/pkg/switch/confd.go +++ b/pkg/switch/confd.go @@ -147,7 +147,7 @@ func (c *ConfD) DiffLink(old *database.VirtualLink, new *database.VirtualLink) b } func (c *ConfD) AddLink(obj *database.VirtualLink) { - worker := GetWorker(obj.Network) + worker := api.GetWorker(obj.Network) if worker == nil { c.out.Warn("ConfD.AddLink network %s not found.", obj.Network) return @@ -179,7 +179,7 @@ func (c *ConfD) AddLink(obj *database.VirtualLink) { } func (c *ConfD) DelLink(obj *database.VirtualLink) { - worker := GetWorker(obj.Network) + worker := api.GetWorker(obj.Network) if worker == nil { c.out.Warn("ConfD.DelLink network %s not found.", obj.Network) return @@ -215,7 +215,7 @@ func (c *ConfD) UpdateName(obj *database.NameCache) { return } c.out.Info("ConfD.UpdateName %s %s", obj.Name, obj.Address) - ListWorker(func(w Networker) { + api.ListWorker(func(w api.Networker) { cfg := w.Config() spec := cfg.Specifies if spec == nil { @@ -235,7 +235,7 @@ func (c *ConfD) AddRoute(obj *database.PrefixRoute) { return } c.out.Cmd("ConfD.DelRoute %v", obj.Network) - worker := GetWorker(obj.Network) + worker := api.GetWorker(obj.Network) if worker == nil { c.out.Warn("ConfD.DelRoute network %s not found.", obj.Network) return @@ -273,7 +273,7 @@ func (c *ConfD) DelRoute(obj *database.PrefixRoute) { return } c.out.Cmd("ConfD.DelRoute %v", obj.Network) - worker := GetWorker(obj.Network) + worker := api.GetWorker(obj.Network) if worker == nil { c.out.Warn("ConfD.DelRoute network %s not found.", obj.Network) return @@ -297,7 +297,7 @@ func (c *ConfD) DelRoute(obj *database.PrefixRoute) { type LinkImpl struct { api api.Switcher out *libol.SubLogger - worker Networker + worker api.Networker } func (l *LinkImpl) Add(obj *database.VirtualLink) { diff --git a/pkg/switch/fabric.go b/pkg/switch/fabric.go index 192ba1a..dad0bd2 100644 --- a/pkg/switch/fabric.go +++ b/pkg/switch/fabric.go @@ -221,7 +221,7 @@ func (w *FabricWorker) Initialize() { } _ = w.ovs.setMode("secure") w.upTables() - ListWorker(func(n Networker) { + api.ListWorker(func(n api.Networker) { if w.IsSlave(n) { n.Initialize() } @@ -441,7 +441,7 @@ func (w *FabricWorker) Start(v api.Switcher) { w.AddTunnel(*tunnel) } w.WorkerImpl.Start(v) - ListWorker(func(n Networker) { + api.ListWorker(func(n api.Networker) { if w.IsSlave(n) { n.Start(v) } @@ -471,7 +471,7 @@ func (w *FabricWorker) DelTunnel(name string) { _ = w.ovs.delPort(name) } -func (w *FabricWorker) IsSlave(n Networker) bool { +func (w *FabricWorker) IsSlave(n api.Networker) bool { cfg := n.Config() if cfg == nil || cfg.Specifies == nil { return false @@ -485,7 +485,7 @@ func (w *FabricWorker) IsSlave(n Networker) bool { func (w *FabricWorker) Stop() { w.out.Info("FabricWorker.Stop") - ListWorker(func(n Networker) { + api.ListWorker(func(n api.Networker) { if w.IsSlave(n) { n.Stop() } diff --git a/pkg/switch/network.go b/pkg/switch/network.go index 859bc4b..37c09b6 100755 --- a/pkg/switch/network.go +++ b/pkg/switch/network.go @@ -13,23 +13,8 @@ import ( "github.com/vishvananda/netlink" ) -type Networker interface { - String() string - ID() string - Initialize() - Start(v api.Switcher) - Stop() - Bridge() cn.Bridger - Config() *co.Network - Subnet() string - Reload(v api.Switcher) - Provider() string -} - -var workers = make(map[string]Networker) - -func NewNetworker(c *co.Network) Networker { - var obj Networker +func NewNetworker(c *co.Network) api.Networker { + var obj api.Networker switch c.Provider { case "esp": obj = NewESPWorker(c) @@ -42,20 +27,10 @@ func NewNetworker(c *co.Network) Networker { default: obj = NewOpenLANWorker(c) } - workers[c.Name] = obj + api.AddWorker(c.Name, obj) return obj } -func GetWorker(name string) Networker { - return workers[name] -} - -func ListWorker(call func(w Networker)) { - for _, worker := range workers { - call(worker) - } -} - type LinuxPort struct { name string // gre:xx, vxlan:xx vlan int @@ -72,7 +47,7 @@ type WorkerImpl struct { setR *cn.IPSet setV *cn.IPSet vpn *OpenVPN - trust *ZeroTrust + ztrust *ZTrust } func NewWorkerApi(c *co.Network) *WorkerImpl { @@ -104,9 +79,9 @@ func (w *WorkerImpl) Initialize() { w.out.Error("WorkImpl.Initialize: create ipset: %s %s", out, err) } w.newVPN() - if w.cfg.ZeroTrust == "enable" { - w.trust = NewZeroTrust(w.cfg.Name, 30) - w.trust.Initialize() + if w.cfg.ZTrust == "enable" { + w.ztrust = NewZTrust(w.cfg.Name, 30) + w.ztrust.Initialize() } } @@ -248,11 +223,11 @@ func (w *WorkerImpl) Start(v api.Switcher) { w.vpn.Start() } - if !(w.vpn == nil || w.trust == nil) { - w.trust.Start() + if !(w.vpn == nil || w.ztrust == nil) { + w.ztrust.Start() fire.Mangle.Pre.AddRule(cn.IpRule{ Input: vpn.Device, - Jump: w.trust.Chain(), + Jump: w.ztrust.Chain(), }) } fire.Start() @@ -307,8 +282,8 @@ func (w *WorkerImpl) DelOutput(bridge string, port *LinuxPort) { func (w *WorkerImpl) Stop() { w.out.Info("WorkerImpl.Stop") w.fire.Stop() - if !(w.vpn == nil || w.trust == nil) { - w.trust.Stop() + if !(w.vpn == nil || w.ztrust == nil) { + w.ztrust.Stop() } if !(w.vpn == nil) { w.vpn.Stop() @@ -489,3 +464,7 @@ func (w *WorkerImpl) newVPN() { obj.Initialize() w.vpn = obj } + +func (w *WorkerImpl) ZTruster() api.ZTruster { + return w.ztrust +} diff --git a/pkg/switch/switch.go b/pkg/switch/switch.go index 073508d..f5305ad 100755 --- a/pkg/switch/switch.go +++ b/pkg/switch/switch.go @@ -8,6 +8,7 @@ import ( "sync" "time" + "github.com/luscis/openlan/pkg/api" "github.com/luscis/openlan/pkg/app" "github.com/luscis/openlan/pkg/cache" co "github.com/luscis/openlan/pkg/config" @@ -96,7 +97,7 @@ type Switch struct { hooks []Hook http *Http server libol.SocketServer - worker map[string]Networker + worker map[string]api.Networker uuid string newTime int64 out *libol.SubLogger @@ -110,7 +111,7 @@ func NewSwitch(c *co.Switch) *Switch { v := &Switch{ cfg: c, fire: network.NewFireWallGlobal(c.FireWall), - worker: make(map[string]Networker, 32), + worker: make(map[string]api.Networker, 32), server: server, newTime: time.Now().Unix(), hooks: make([]Hook, 0, 64), diff --git a/pkg/switch/zerotrust.go b/pkg/switch/zerotrust.go deleted file mode 100644 index fceb4d3..0000000 --- a/pkg/switch/zerotrust.go +++ /dev/null @@ -1,162 +0,0 @@ -package cswitch - -import ( - "fmt" - "time" - - "github.com/luscis/openlan/pkg/libol" - "github.com/luscis/openlan/pkg/network" - cn "github.com/luscis/openlan/pkg/network" -) - -type KnockRule struct { - createAt int64 - protocol string - dest string - destPort string - rule *cn.IpRule -} - -func (r *KnockRule) Id() string { - return fmt.Sprintf("%s:%s:%s", r.protocol, r.dest, r.destPort) -} - -type ZeroGuest struct { - network string - username string - device string - sources map[string]string - rules map[string]*KnockRule - chain *cn.FireWallChain -} - -func NewZeroGuest(network, name string) *ZeroGuest { - return &ZeroGuest{ - network: network, - username: name, - sources: make(map[string]string, 2), - rules: make(map[string]*KnockRule, 1024), - } -} - -func (z *ZeroGuest) Chain() string { - return "ZTT_" + z.network + "_" + z.username -} - -func (z *ZeroGuest) Initialize() { - z.chain = cn.NewFireWallChain(z.Chain(), network.TMangle, "") -} - -func (z *ZeroGuest) AddSource(source string) { - z.sources[source] = source -} - -func (z *ZeroGuest) DelSource(source string) { - if _, ok := z.sources[source]; ok { - delete(z.sources, source) - } -} - -func (z *ZeroGuest) AddRule(rule *KnockRule) { - z.rules[rule.Id()] = rule - z.chain.AddRuleX(cn.IpRule{ - Dest: rule.dest, - DstPort: rule.destPort, - Jump: "ACCEPT", - Comment: time.Now().Local().String(), - }) -} - -func (z *ZeroGuest) DelRule(rule *KnockRule) { - if _, ok := z.rules[rule.Id()]; ok { - delete(z.rules, rule.Id()) - } -} - -type ZeroTrust struct { - network string - expire int - guests map[string]*ZeroGuest - chain *cn.FireWallChain - out *libol.SubLogger -} - -func NewZeroTrust(network string, expire int) *ZeroTrust { - return &ZeroTrust{ - network: network, - expire: expire, - out: libol.NewSubLogger(network), - guests: make(map[string]*ZeroGuest, 32), - } -} - -func (z *ZeroTrust) Chain() string { - return "ZTT_" + z.network -} - -func (z *ZeroTrust) Initialize() { - z.chain = cn.NewFireWallChain(z.Chain(), network.TMangle, "") - z.chain.AddRule(cn.IpRule{ - Comment: "Zero Trust Default", - Jump: "DROP", - }) -} - -func (z *ZeroTrust) Knock(name string, dest, protocol, destPort string) { - guest, ok := z.guests[name] - if !ok { - z.out.Warn("ZeroTrust.Knock: not found %s", name) - return - } - guest.AddRule(&KnockRule{ - protocol: protocol, - dest: dest, - destPort: destPort, - createAt: time.Now().Unix(), - }) -} - -func (z *ZeroTrust) Update() { - //TODO expire knock rules. -} - -func (z *ZeroTrust) AddGuest(name, device, source string) { - guest, ok := z.guests[name] - if !ok { - guest = NewZeroGuest(z.network, name) - guest.Initialize() - z.guests[name] = guest - } - guest.AddSource(source) - z.chain.AddRuleX(cn.IpRule{ - Input: device, - Source: source, - Comment: guest.username + "." + guest.network, - Jump: guest.Chain(), - Order: "-I", - }) -} - -func (z *ZeroTrust) DelGuest(name, device, source string) { - guest, ok := z.guests[name] - if !ok { - return - } - z.chain.DelRuleX(cn.IpRule{ - Input: device, - Source: source, - Comment: guest.username + "." + guest.network, - Jump: guest.Chain(), - }) - guest.DelSource(source) -} - -func (z *ZeroTrust) Start() { - z.out.Info("ZeroTrust.Start") - z.chain.Install() -} - -func (z *ZeroTrust) Stop() { - z.out.Info("ZeroTrust.Stop") - z.chain.Cancel() -} diff --git a/pkg/switch/ztrust.go b/pkg/switch/ztrust.go new file mode 100644 index 0000000..9e007b6 --- /dev/null +++ b/pkg/switch/ztrust.go @@ -0,0 +1,217 @@ +package cswitch + +import ( + "fmt" + "time" + + "github.com/luscis/openlan/pkg/libol" + "github.com/luscis/openlan/pkg/network" + cn "github.com/luscis/openlan/pkg/network" +) + +type KnockRule struct { + createAt int64 + age int + protocol string + destination string + port string + rule *cn.IpRule +} + +func (r *KnockRule) Id() string { + return fmt.Sprintf("%s:%s:%s", r.protocol, r.destination, r.port) +} + +type ZGuest struct { + network string + username string + sources map[string]string + rules map[string]*KnockRule + chain *cn.FireWallChain + out *libol.SubLogger +} + +func NewZGuest(network, name string) *ZGuest { + return &ZGuest{ + network: network, + username: name, + sources: make(map[string]string, 2), + rules: make(map[string]*KnockRule, 1024), + out: libol.NewSubLogger(name + "@" + network), + } +} + +func (g *ZGuest) Chain() string { + return "ZTT_" + g.network + "-" + g.username +} + +func (g *ZGuest) Initialize() { + g.chain = cn.NewFireWallChain(g.Chain(), network.TMangle, "") +} + +func (g *ZGuest) Start() { + g.chain.Install() +} + +func (g *ZGuest) AddSource(source string) { + g.sources[source] = source +} + +func (g *ZGuest) DelSource(source string) { + if _, ok := g.sources[source]; ok { + delete(g.sources, source) + } +} + +func (g *ZGuest) AddRuleX(rule cn.IpRule) { + if err := g.chain.AddRuleX(rule); err != nil { + g.out.Warn("ZTrust.AddRuleX: %s", err) + } +} + +func (g *ZGuest) DelRuleX(rule cn.IpRule) { + if err := g.chain.DelRuleX(rule); err != nil { + g.out.Warn("ZTrust.DelRuleX: %s", err) + } +} + +func (g *ZGuest) AddRule(rule *KnockRule) { + g.rules[rule.Id()] = rule + g.AddRuleX(cn.IpRule{ + Dest: rule.destination, + DstPort: rule.port, + Proto: rule.protocol, + Jump: "ACCEPT", + Comment: "Knock at " + time.Now().UTC().String(), + }) +} + +func (g *ZGuest) DelRule(rule *KnockRule) { + if _, ok := g.rules[rule.Id()]; ok { + delete(g.rules, rule.Id()) + } + g.DelRuleX(cn.IpRule{ + Proto: rule.protocol, + Dest: rule.destination, + DstPort: rule.port, + Jump: "ACCEPT", + Comment: "Knock at " + time.Now().Local().String(), + }) +} + +func (g *ZGuest) Stop() { + g.chain.Cancel() +} + +type ZTrust struct { + network string + expire int + guests map[string]*ZGuest + chain *cn.FireWallChain + out *libol.SubLogger +} + +func NewZTrust(network string, expire int) *ZTrust { + return &ZTrust{ + network: network, + expire: expire, + out: libol.NewSubLogger(network), + guests: make(map[string]*ZGuest, 32), + } +} + +func (z *ZTrust) Chain() string { + return "ZTT_" + z.network +} + +func (z *ZTrust) Initialize() { + z.chain = cn.NewFireWallChain(z.Chain(), network.TMangle, "") + z.chain.AddRule(cn.IpRule{ + Comment: "ZTrust Default", + Jump: "DROP", + }) +} + +func (z *ZTrust) Knock(name string, protocol, dest, port string, age int) error { + guest, ok := z.guests[name] + if !ok { + return libol.NewErr("Knock: not found %s", name) + } + guest.AddRule(&KnockRule{ + protocol: protocol, + destination: dest, + port: port, + createAt: time.Now().Unix(), + age: age, + }) + return nil +} + +func (z *ZTrust) Update() { + //TODO expire knock rules. +} + +func (z *ZTrust) AddRuleX(rule cn.IpRule) { + if err := z.chain.AddRuleX(rule); err != nil { + z.out.Warn("ZTrust.AddRuleX: %s", err) + } +} + +func (z *ZTrust) DelRuleX(rule cn.IpRule) { + if err := z.chain.DelRuleX(rule); err != nil { + z.out.Warn("ZTrust.DelRuleX: %s", err) + } +} + +func (z *ZTrust) AddGuest(name, source string) error { + z.out.Info("ZTrust.AddGuest: %s %s", name, source) + if source == "" { + return libol.NewErr("AddGuest: invalid source") + } + guest, ok := z.guests[name] + if !ok { + guest = NewZGuest(z.network, name) + guest.Initialize() + guest.Start() + z.guests[name] = guest + } + guest.AddSource(source) + z.AddRuleX(cn.IpRule{ + Source: source, + Comment: "User " + guest.username + "@" + guest.network, + Jump: guest.Chain(), + Order: "-I", + }) + return nil +} + +func (z *ZTrust) DelGuest(name, source string) error { + if source == "" { + return libol.NewErr("DelGuest: invalid source") + } + guest, ok := z.guests[name] + if !ok { + return libol.NewErr("DelGuest: not found %s", name) + } + z.out.Info("ZTrust.DelGuest: %s %s", name, source) + z.DelRuleX(cn.IpRule{ + Source: source, + Comment: guest.username + "." + guest.network, + Jump: guest.Chain(), + }) + guest.DelSource(source) + return nil +} + +func (z *ZTrust) Start() { + z.out.Info("ZTrust.Start") + z.chain.Install() +} + +func (z *ZTrust) Stop() { + z.out.Info("ZTrust.Stop") + z.chain.Cancel() + for _, guest := range z.guests { + guest.Stop() + } +}