fea: add commands for zero trust

This commit is contained in:
Daniel Ding
2024-01-01 22:38:07 +08:00
parent e856c6dfe0
commit 9a039a6d3c
18 changed files with 697 additions and 228 deletions

View File

@@ -1,6 +1,9 @@
package api package api
import "os" import (
"os"
"strings"
)
func GetEnv(key, value string) string { func GetEnv(key, value string) string {
val := os.Getenv(key) val := os.Getenv(key)
@@ -9,3 +12,19 @@ func GetEnv(key, value string) string {
} }
return val 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, ""
}

View File

@@ -44,4 +44,6 @@ func Commands(app *api.App) {
Policy{}.Commands(app) Policy{}.Commands(app)
Version{}.Commands(app) Version{}.Commands(app)
Log{}.Commands(app) Log{}.Commands(app)
ZGuest{}.Commands(app)
Knock{}.Commands(app)
} }

112
cmd/api/v5/knock.go Executable file
View File

@@ -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,
},
},
})
}

View File

@@ -51,7 +51,7 @@ func (u Point) Commands(app *api.App) {
app.Command(&cli.Command{ app.Command(&cli.Command{
Name: "point", Name: "point",
Aliases: []string{"ap"}, Aliases: []string{"ap"},
Usage: "Point connected to this", Usage: "Point accessed to switch",
Subcommands: []*cli.Command{ Subcommands: []*cli.Command{
{ {
Name: "list", Name: "list",

View File

@@ -26,22 +26,17 @@ func (u User) Url(prefix, name string) string {
func (u User) Add(c *cli.Context) error { func (u User) Add(c *cli.Context) error {
username := c.String("name") username := c.String("name")
if !strings.Contains(username, "@") {
return libol.NewErr("invalid username")
}
user := &schema.User{ user := &schema.User{
Name: username, Name: username,
Password: c.String("password"), Password: c.String("password"),
Role: c.String("role"), Role: c.String("role"),
Lease: c.String("lease"), Lease: c.String("lease"),
} }
if user.Name == "" { user.Name, user.Network = api.SplitName(username)
return libol.NewErr("name is empty") url := u.Url(c.String("url"), username)
}
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)
clt := u.NewHttp(c.String("token")) clt := u.NewHttp(c.String("token"))
if err := clt.PostJSON(url, user, nil); err != nil { if err := clt.PostJSON(url, user, nil); err != nil {
return err return err

110
cmd/api/v5/zguest.go Executable file
View File

@@ -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,
},
},
})
}

View File

@@ -1,12 +1,13 @@
package api package api
import ( import (
"net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/luscis/openlan/pkg/cache" "github.com/luscis/openlan/pkg/cache"
"github.com/luscis/openlan/pkg/libol" "github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/models" "github.com/luscis/openlan/pkg/models"
"github.com/luscis/openlan/pkg/schema" "github.com/luscis/openlan/pkg/schema"
"net/http"
) )
type Link struct { 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) { func (h Link) Get(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
libol.Info("GetPoint %s", vars["id"]) libol.Info("Link.Get %s", vars["id"])
link := cache.Link.Get(vars["id"]) link := cache.Link.Get(vars["id"])
if link != nil { if link != nil {

View File

@@ -1,8 +1,9 @@
package api package api
import ( import (
"github.com/luscis/openlan/pkg/config" co "github.com/luscis/openlan/pkg/config"
"github.com/luscis/openlan/pkg/libol" "github.com/luscis/openlan/pkg/libol"
cn "github.com/luscis/openlan/pkg/network"
"github.com/luscis/openlan/pkg/schema" "github.com/luscis/openlan/pkg/schema"
) )
@@ -10,7 +11,7 @@ type Switcher interface {
UUID() string UUID() string
UpTime() int64 UpTime() int64
Alias() string Alias() string
Config() *config.Switch Config() *co.Switch
Server() libol.SocketServer Server() libol.SocketServer
Reload() Reload()
Save() Save()
@@ -28,3 +29,39 @@ func NewWorkerSchema(s Switcher) schema.Worker {
Protocol: protocol, 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)
}
}

View File

@@ -22,4 +22,5 @@ func Add(router *mux.Router, switcher Switcher) {
Version{}.Router(router) Version{}.Router(router)
Log{}.Router(router) Log{}.Router(router)
OpenAPI{}.Router(router) OpenAPI{}.Router(router)
ZTrust{}.Router(router)
} }

140
pkg/api/ztrust.go Executable file
View File

@@ -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
}
}

View File

@@ -23,7 +23,7 @@ type Network struct {
Specifies interface{} `json:"specifies,omitempty"` Specifies interface{} `json:"specifies,omitempty"`
Dhcp string `json:"dhcp,omitempty"` Dhcp string `json:"dhcp,omitempty"`
Outputs []Output `json:"outputs"` Outputs []Output `json:"outputs"`
ZeroTrust string `json:"zerotrust"` ZTrust string `json:"ztrust"`
} }
func (n *Network) NewSpecifies() interface{} { func (n *Network) NewSpecifies() interface{} {

17
pkg/schema/ztrust.go Executable file
View File

@@ -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"`
}

View File

@@ -147,7 +147,7 @@ func (c *ConfD) DiffLink(old *database.VirtualLink, new *database.VirtualLink) b
} }
func (c *ConfD) AddLink(obj *database.VirtualLink) { func (c *ConfD) AddLink(obj *database.VirtualLink) {
worker := GetWorker(obj.Network) worker := api.GetWorker(obj.Network)
if worker == nil { if worker == nil {
c.out.Warn("ConfD.AddLink network %s not found.", obj.Network) c.out.Warn("ConfD.AddLink network %s not found.", obj.Network)
return return
@@ -179,7 +179,7 @@ func (c *ConfD) AddLink(obj *database.VirtualLink) {
} }
func (c *ConfD) DelLink(obj *database.VirtualLink) { func (c *ConfD) DelLink(obj *database.VirtualLink) {
worker := GetWorker(obj.Network) worker := api.GetWorker(obj.Network)
if worker == nil { if worker == nil {
c.out.Warn("ConfD.DelLink network %s not found.", obj.Network) c.out.Warn("ConfD.DelLink network %s not found.", obj.Network)
return return
@@ -215,7 +215,7 @@ func (c *ConfD) UpdateName(obj *database.NameCache) {
return return
} }
c.out.Info("ConfD.UpdateName %s %s", obj.Name, obj.Address) c.out.Info("ConfD.UpdateName %s %s", obj.Name, obj.Address)
ListWorker(func(w Networker) { api.ListWorker(func(w api.Networker) {
cfg := w.Config() cfg := w.Config()
spec := cfg.Specifies spec := cfg.Specifies
if spec == nil { if spec == nil {
@@ -235,7 +235,7 @@ func (c *ConfD) AddRoute(obj *database.PrefixRoute) {
return return
} }
c.out.Cmd("ConfD.DelRoute %v", obj.Network) c.out.Cmd("ConfD.DelRoute %v", obj.Network)
worker := GetWorker(obj.Network) worker := api.GetWorker(obj.Network)
if worker == nil { if worker == nil {
c.out.Warn("ConfD.DelRoute network %s not found.", obj.Network) c.out.Warn("ConfD.DelRoute network %s not found.", obj.Network)
return return
@@ -273,7 +273,7 @@ func (c *ConfD) DelRoute(obj *database.PrefixRoute) {
return return
} }
c.out.Cmd("ConfD.DelRoute %v", obj.Network) c.out.Cmd("ConfD.DelRoute %v", obj.Network)
worker := GetWorker(obj.Network) worker := api.GetWorker(obj.Network)
if worker == nil { if worker == nil {
c.out.Warn("ConfD.DelRoute network %s not found.", obj.Network) c.out.Warn("ConfD.DelRoute network %s not found.", obj.Network)
return return
@@ -297,7 +297,7 @@ func (c *ConfD) DelRoute(obj *database.PrefixRoute) {
type LinkImpl struct { type LinkImpl struct {
api api.Switcher api api.Switcher
out *libol.SubLogger out *libol.SubLogger
worker Networker worker api.Networker
} }
func (l *LinkImpl) Add(obj *database.VirtualLink) { func (l *LinkImpl) Add(obj *database.VirtualLink) {

View File

@@ -221,7 +221,7 @@ func (w *FabricWorker) Initialize() {
} }
_ = w.ovs.setMode("secure") _ = w.ovs.setMode("secure")
w.upTables() w.upTables()
ListWorker(func(n Networker) { api.ListWorker(func(n api.Networker) {
if w.IsSlave(n) { if w.IsSlave(n) {
n.Initialize() n.Initialize()
} }
@@ -441,7 +441,7 @@ func (w *FabricWorker) Start(v api.Switcher) {
w.AddTunnel(*tunnel) w.AddTunnel(*tunnel)
} }
w.WorkerImpl.Start(v) w.WorkerImpl.Start(v)
ListWorker(func(n Networker) { api.ListWorker(func(n api.Networker) {
if w.IsSlave(n) { if w.IsSlave(n) {
n.Start(v) n.Start(v)
} }
@@ -471,7 +471,7 @@ func (w *FabricWorker) DelTunnel(name string) {
_ = w.ovs.delPort(name) _ = w.ovs.delPort(name)
} }
func (w *FabricWorker) IsSlave(n Networker) bool { func (w *FabricWorker) IsSlave(n api.Networker) bool {
cfg := n.Config() cfg := n.Config()
if cfg == nil || cfg.Specifies == nil { if cfg == nil || cfg.Specifies == nil {
return false return false
@@ -485,7 +485,7 @@ func (w *FabricWorker) IsSlave(n Networker) bool {
func (w *FabricWorker) Stop() { func (w *FabricWorker) Stop() {
w.out.Info("FabricWorker.Stop") w.out.Info("FabricWorker.Stop")
ListWorker(func(n Networker) { api.ListWorker(func(n api.Networker) {
if w.IsSlave(n) { if w.IsSlave(n) {
n.Stop() n.Stop()
} }

View File

@@ -13,23 +13,8 @@ import (
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
) )
type Networker interface { func NewNetworker(c *co.Network) api.Networker {
String() string var obj api.Networker
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
switch c.Provider { switch c.Provider {
case "esp": case "esp":
obj = NewESPWorker(c) obj = NewESPWorker(c)
@@ -42,20 +27,10 @@ func NewNetworker(c *co.Network) Networker {
default: default:
obj = NewOpenLANWorker(c) obj = NewOpenLANWorker(c)
} }
workers[c.Name] = obj api.AddWorker(c.Name, obj)
return 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 { type LinuxPort struct {
name string // gre:xx, vxlan:xx name string // gre:xx, vxlan:xx
vlan int vlan int
@@ -72,7 +47,7 @@ type WorkerImpl struct {
setR *cn.IPSet setR *cn.IPSet
setV *cn.IPSet setV *cn.IPSet
vpn *OpenVPN vpn *OpenVPN
trust *ZeroTrust ztrust *ZTrust
} }
func NewWorkerApi(c *co.Network) *WorkerImpl { 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.out.Error("WorkImpl.Initialize: create ipset: %s %s", out, err)
} }
w.newVPN() w.newVPN()
if w.cfg.ZeroTrust == "enable" { if w.cfg.ZTrust == "enable" {
w.trust = NewZeroTrust(w.cfg.Name, 30) w.ztrust = NewZTrust(w.cfg.Name, 30)
w.trust.Initialize() w.ztrust.Initialize()
} }
} }
@@ -248,11 +223,11 @@ func (w *WorkerImpl) Start(v api.Switcher) {
w.vpn.Start() w.vpn.Start()
} }
if !(w.vpn == nil || w.trust == nil) { if !(w.vpn == nil || w.ztrust == nil) {
w.trust.Start() w.ztrust.Start()
fire.Mangle.Pre.AddRule(cn.IpRule{ fire.Mangle.Pre.AddRule(cn.IpRule{
Input: vpn.Device, Input: vpn.Device,
Jump: w.trust.Chain(), Jump: w.ztrust.Chain(),
}) })
} }
fire.Start() fire.Start()
@@ -307,8 +282,8 @@ func (w *WorkerImpl) DelOutput(bridge string, port *LinuxPort) {
func (w *WorkerImpl) Stop() { func (w *WorkerImpl) Stop() {
w.out.Info("WorkerImpl.Stop") w.out.Info("WorkerImpl.Stop")
w.fire.Stop() w.fire.Stop()
if !(w.vpn == nil || w.trust == nil) { if !(w.vpn == nil || w.ztrust == nil) {
w.trust.Stop() w.ztrust.Stop()
} }
if !(w.vpn == nil) { if !(w.vpn == nil) {
w.vpn.Stop() w.vpn.Stop()
@@ -489,3 +464,7 @@ func (w *WorkerImpl) newVPN() {
obj.Initialize() obj.Initialize()
w.vpn = obj w.vpn = obj
} }
func (w *WorkerImpl) ZTruster() api.ZTruster {
return w.ztrust
}

View File

@@ -8,6 +8,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/luscis/openlan/pkg/api"
"github.com/luscis/openlan/pkg/app" "github.com/luscis/openlan/pkg/app"
"github.com/luscis/openlan/pkg/cache" "github.com/luscis/openlan/pkg/cache"
co "github.com/luscis/openlan/pkg/config" co "github.com/luscis/openlan/pkg/config"
@@ -96,7 +97,7 @@ type Switch struct {
hooks []Hook hooks []Hook
http *Http http *Http
server libol.SocketServer server libol.SocketServer
worker map[string]Networker worker map[string]api.Networker
uuid string uuid string
newTime int64 newTime int64
out *libol.SubLogger out *libol.SubLogger
@@ -110,7 +111,7 @@ func NewSwitch(c *co.Switch) *Switch {
v := &Switch{ v := &Switch{
cfg: c, cfg: c,
fire: network.NewFireWallGlobal(c.FireWall), fire: network.NewFireWallGlobal(c.FireWall),
worker: make(map[string]Networker, 32), worker: make(map[string]api.Networker, 32),
server: server, server: server,
newTime: time.Now().Unix(), newTime: time.Now().Unix(),
hooks: make([]Hook, 0, 64), hooks: make([]Hook, 0, 64),

View File

@@ -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()
}

217
pkg/switch/ztrust.go Normal file
View File

@@ -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()
}
}