mirror of
https://github.com/luscis/openlan.git
synced 2025-10-05 08:36:59 +08:00
304 lines
5.7 KiB
Go
304 lines
5.7 KiB
Go
package cswitch
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/luscis/openlan/pkg/libol"
|
|
cn "github.com/luscis/openlan/pkg/network"
|
|
"github.com/luscis/openlan/pkg/schema"
|
|
)
|
|
|
|
type KnockRule struct {
|
|
createAt time.Time
|
|
age int64
|
|
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)
|
|
}
|
|
|
|
func (r *KnockRule) Expire() bool {
|
|
now := time.Now()
|
|
if r.createAt.Unix()+int64(r.age) < now.Unix() {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (r *KnockRule) Rule() cn.IPRule {
|
|
if r.rule == nil {
|
|
r.rule = &cn.IPRule{
|
|
Dest: r.destination,
|
|
DstPort: r.port,
|
|
Proto: r.protocol,
|
|
Comment: "Knock at " + r.createAt.UTC().String(),
|
|
}
|
|
}
|
|
return *r.rule
|
|
}
|
|
|
|
type ZGuest struct {
|
|
network string
|
|
username string
|
|
source string
|
|
rules map[string]*KnockRule
|
|
chain *cn.FireWallChain
|
|
out *libol.SubLogger
|
|
lock sync.Mutex
|
|
}
|
|
|
|
func NewZGuest(network, name, source string) *ZGuest {
|
|
return &ZGuest{
|
|
network: network,
|
|
username: name,
|
|
source: source,
|
|
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) Start() {
|
|
g.chain = cn.NewFireWallChain(g.Chain(), cn.TMangle, "")
|
|
g.chain.Install()
|
|
}
|
|
|
|
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.lock.Lock()
|
|
defer g.lock.Unlock()
|
|
|
|
if dst, ok := g.rules[rule.Id()]; !ok {
|
|
g.addRuleX(rule.Rule())
|
|
g.rules[rule.Id()] = rule
|
|
} else {
|
|
dst.age = rule.age
|
|
dst.createAt = rule.createAt
|
|
}
|
|
}
|
|
|
|
func (g *ZGuest) DelRule(rule *KnockRule) {
|
|
g.lock.Lock()
|
|
defer g.lock.Unlock()
|
|
|
|
if _, ok := g.rules[rule.Id()]; ok {
|
|
g.delRuleX(rule.Rule())
|
|
delete(g.rules, rule.Id())
|
|
}
|
|
}
|
|
|
|
func (g *ZGuest) Stop() {
|
|
g.lock.Lock()
|
|
defer g.lock.Unlock()
|
|
|
|
g.flush()
|
|
g.chain.Cancel()
|
|
}
|
|
|
|
func (g *ZGuest) Clear() {
|
|
g.lock.Lock()
|
|
defer g.lock.Unlock()
|
|
|
|
removed := make([]*KnockRule, 0, 32)
|
|
for _, rule := range g.rules {
|
|
if rule.Expire() {
|
|
removed = append(removed, rule)
|
|
}
|
|
}
|
|
for _, rule := range removed {
|
|
g.out.Info("ZTrust.Clear: %s", rule.Id())
|
|
delete(g.rules, rule.Id())
|
|
g.delRuleX(rule.Rule())
|
|
}
|
|
}
|
|
|
|
func (g *ZGuest) flush() {
|
|
for _, rule := range g.rules {
|
|
g.delRuleX(rule.Rule())
|
|
}
|
|
}
|
|
|
|
func (g *ZGuest) Flush() {
|
|
g.lock.Lock()
|
|
defer g.lock.Unlock()
|
|
|
|
g.flush()
|
|
}
|
|
|
|
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(), cn.TMangle, "")
|
|
z.chain.AddRule(cn.IPRule{
|
|
Comment: "ZTrust Deny All",
|
|
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)
|
|
}
|
|
rule := &KnockRule{
|
|
protocol: protocol,
|
|
destination: dest,
|
|
port: port,
|
|
createAt: time.Now(),
|
|
age: int64(age),
|
|
}
|
|
z.out.Info("Knock: %s %s", name, rule.Id())
|
|
guest.AddRule(rule)
|
|
return nil
|
|
}
|
|
|
|
func (z *ZTrust) Update() {
|
|
for {
|
|
for _, guest := range z.guests {
|
|
guest.Clear()
|
|
}
|
|
|
|
time.Sleep(time.Second * 3)
|
|
}
|
|
}
|
|
|
|
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 {
|
|
return nil
|
|
}
|
|
|
|
guest = NewZGuest(z.network, name, source)
|
|
guest.Start()
|
|
z.addRuleX(cn.IPRule{
|
|
Source: guest.source,
|
|
Comment: "User " + guest.username + "@" + guest.network,
|
|
Jump: guest.Chain(),
|
|
Order: "-I",
|
|
})
|
|
z.guests[name] = guest
|
|
|
|
return nil
|
|
}
|
|
|
|
func (z *ZTrust) DelGuest(name, source string) error {
|
|
guest, ok := z.guests[name]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
z.out.Info("ZTrust.DelGuest: %s %s", name, source)
|
|
|
|
z.delRuleX(cn.IPRule{
|
|
Source: guest.source,
|
|
Comment: "User " + guest.username + "@" + guest.network,
|
|
Jump: guest.Chain(),
|
|
})
|
|
guest.Stop()
|
|
delete(z.guests, name)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (z *ZTrust) Start() {
|
|
z.out.Info("ZTrust.Start")
|
|
z.chain.Install()
|
|
libol.Go(z.Update)
|
|
}
|
|
|
|
func (z *ZTrust) Stop() {
|
|
z.out.Info("ZTrust.Stop")
|
|
z.chain.Cancel()
|
|
for _, guest := range z.guests {
|
|
guest.Stop()
|
|
}
|
|
}
|
|
|
|
func (z *ZTrust) ListGuest(call func(obj schema.ZGuest)) {
|
|
for _, guest := range z.guests {
|
|
obj := schema.ZGuest{
|
|
Name: guest.username,
|
|
Network: guest.network,
|
|
Address: guest.source,
|
|
}
|
|
call(obj)
|
|
}
|
|
}
|
|
|
|
func (z *ZTrust) ListKnock(name string, call func(obj schema.KnockRule)) {
|
|
guest, ok := z.guests[name]
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
now := time.Now()
|
|
for _, rule := range guest.rules {
|
|
createAt := rule.createAt
|
|
obj := schema.KnockRule{
|
|
Name: name,
|
|
Network: z.network,
|
|
Protocol: rule.protocol,
|
|
Dest: rule.destination,
|
|
Port: rule.port,
|
|
CreateAt: createAt.Unix(),
|
|
Age: int(rule.age + createAt.Unix() - now.Unix()),
|
|
}
|
|
call(obj)
|
|
}
|
|
}
|