Skip to content

Commit

Permalink
refactoring + use gorm as backend + work as daemon
Browse files Browse the repository at this point in the history
- Global refactoring.
- Use DBs to hold information using gorm.
- Allow to work as daemon (server) to store the nodes information.
- Added more usage examples to the help.
  • Loading branch information
gustavo-iniguez-goya committed Dec 30, 2020
1 parent e34fb01 commit f1d406f
Show file tree
Hide file tree
Showing 18 changed files with 1,122 additions and 427 deletions.
2 changes: 1 addition & 1 deletion server/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func socketIsReady(proto, port string) bool {
}

// StartServer start listening for incoming nodes/clients.
func startServer(client *Client, proto, port string) {
func StartServer(client *Client, proto, port string) {
if socketIsReady(proto, port) == false {
return
}
Expand Down
64 changes: 62 additions & 2 deletions server/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import (
"github.com/evilsocket/opensnitch/daemon/ui/protocol"
"github.com/gustavo-iniguez-goya/opensnitch/daemon/log"
"github.com/gustavo-iniguez-goya/opensnitch/server/api/nodes"
"github.com/gustavo-iniguez-goya/opensnitch/server/api/storage"
"golang.org/x/net/context"
"sync"
)

// Client struct groups the API functionality to communicate with the nodes
type Client struct {
sync.RWMutex
db *storage.Storage
workAsDaemon bool
lastStats *protocol.Statistics
nodesChan chan bool
rulesInChan chan *protocol.Connection
Expand Down Expand Up @@ -43,13 +46,18 @@ const (
)

// NewClient setups a new client and starts the server to listen for new nodes.
func NewClient(serverProto, serverPort string) *Client {
func NewClient(serverProto, serverPort string, asDaemon bool, db *storage.Storage) *Client {
c := &Client{
db: db,
workAsDaemon: asDaemon,
nodesChan: make(chan bool),
rulesInChan: make(chan *protocol.Connection, 1),
rulesOutChan: make(chan *protocol.Rule, 1),
}
go startServer(c, serverProto, serverPort)
if asDaemon == false {
go StartServer(c, serverProto, serverPort)
}

return c
}

Expand All @@ -60,10 +68,55 @@ func (c *Client) UpdateStats(ctx context.Context, stats *protocol.Statistics) {
}
c.Lock()
defer c.Unlock()

nodeAddr := nodes.GetAddr(ctx)
c.lastStats = stats
if c.db != nil {
c.db.Update(nodeAddr, stats)
}
nodes.UpdateStats(ctx, stats)
}

// GetStats gets global stats from the db
func (c *Client) GetStats() (stats *[]storage.Statistics) {
if c.db != nil {
stats = c.db.GetStats()
}
return stats
}

// GetNodeStats gets global stats from the db
func (c *Client) GetNodeStats() (nodes *[]storage.Node) {
if c.db != nil {
nodes = c.db.GetNodeStats()
}
return nodes
}

// GetEvents gets events from the db
func (c *Client) GetEvents(order string, limit int) (events *[]storage.Connection) {
if c.db != nil {
events = c.db.GetEvents(order, limit)
}
return events
}

// GetEventsByType returns the list events from the db, by type.
func (c *Client) GetEventsByType(viewType, order string, limit int) (events *[]storage.EventByType) {
if c.db != nil {
events = c.db.GetEventsByType(viewType, order, limit)
}
return events
}

// GetRules returns the list of rules from the db.
func (c *Client) GetRules(order string, limit int) (rules *[]storage.Rule) {
if c.db != nil {
rules = c.db.GetRules(order, limit)
}
return rules
}

// GetLastStats returns latest stasts from a node.
func (c *Client) GetLastStats() *protocol.Statistics {
c.RLock()
Expand All @@ -77,6 +130,10 @@ func (c *Client) GetLastStats() *protocol.Statistics {
// A client must consume data on that channel, and send the response via the
// rulesOutChan channel.
func (c *Client) AskRule(ctx context.Context, con *protocol.Connection) chan *protocol.Rule {
if c.workAsDaemon {
c.rulesOutChan <- nil
return c.rulesOutChan
}
c.rulesInChan <- con
return c.rulesOutChan
}
Expand All @@ -85,6 +142,9 @@ func (c *Client) AskRule(ctx context.Context, con *protocol.Connection) chan *pr
func (c *Client) AddNewNode(ctx context.Context, nodeConf *protocol.ClientConfig) {
log.Info("AddNewNode: %s - %s", nodeConf.Name, nodeConf.Version)
nodes.Add(ctx, nodeConf)
if c.db != nil {
c.db.AddNode(nodes.GetAddr(ctx), nodeConf)
}
c.nodesChan <- true
}

Expand Down
7 changes: 7 additions & 0 deletions server/api/go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
module github.com/gustavo-iniguez-goya/opensnitch/server/api

go 1.14

require (
github.com/evilsocket/opensnitch/daemon v0.0.0-20201224192838-75a08245a966
gorm.io/driver/postgres v1.0.6
gorm.io/driver/sqlite v1.1.4
gorm.io/gorm v1.20.9
)
14 changes: 14 additions & 0 deletions server/api/nodes/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package nodes

import (
"fmt"
"sync"
"time"

"github.com/evilsocket/opensnitch/daemon/ui/protocol"
Expand All @@ -19,6 +20,7 @@ var (
)

type node struct {
sync.RWMutex
// proto:host
addr string
ctx context.Context
Expand All @@ -44,6 +46,9 @@ func NewNode(ctx context.Context, addr string, nodeConf *protocol.ClientConfig)
}

func (n *node) String() string {
n.RLock()
defer n.RUnlock()

return fmt.Sprintf("[%v] - [%-20s] - [%-24s] - [%s] - [%s]", n.lastSeen.Format(time.Stamp), n.addr, n.status, n.config.Version, n.config.Name)
}

Expand Down Expand Up @@ -71,15 +76,24 @@ func (n *node) SendNotification(notif *protocol.Notification) {
}

func (n *node) UpdateStats(stats *protocol.Statistics) {
n.Lock()
defer n.Unlock()

n.stats = stats
n.lastSeen = time.Now()
}

func (n *node) GetStats() *protocol.Statistics {
n.Lock()
defer n.Unlock()

return n.stats
}

func (n *node) GetConfig() *protocol.ClientConfig {
n.RLock()
defer n.RUnlock()

return n.config
}

Expand Down
8 changes: 6 additions & 2 deletions server/api/nodes/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,15 @@ func GetPeer(ctx context.Context) *peer.Peer {
func GetAddr(ctx context.Context) (addr string) {
p := GetPeer(ctx)
host, _, err := net.SplitHostPort(p.Addr.String())
if err != nil {
if err != nil && p.Addr.String() == "@" {
host = "localhost"
addr = "unix://" + host
} else if err != nil {
log.Error("nodes.GetAddr() can not get noe address, addr:", p.Addr.String())
return ""
}
addr = p.Addr.Network() + ":" + host
return addr
return
}

// GetAll nodes.
Expand Down
138 changes: 138 additions & 0 deletions server/api/storage/proto_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package storage

import (
"github.com/evilsocket/opensnitch/daemon/ui/protocol"
"strings"
"time"
)

func (s *Storage) getProtoStats(addr string, stats *protocol.Statistics) *Statistics {
return &Statistics{
Node: addr,
DaemonVersion: stats.DaemonVersion,
Rules: stats.Rules,
Uptime: stats.Uptime,
DNSResponses: stats.DnsResponses,
Connections: stats.Connections,
Ignored: stats.Ignored,
Accepted: stats.Accepted,
Dropped: stats.Dropped,
RuleHits: stats.RuleHits,
RuleMisses: stats.RuleMisses,
}
}

func (s *Storage) getProtoEvents(addr string, stats *protocol.Statistics) (*[]Rule, *[]Connection) {
var conns []Connection
var opers []Rule
for _, ev := range stats.Events {
opers = append([]Rule{
Rule{
Node: addr,
Name: ev.Rule.Name,
Enabled: ev.Rule.Enabled,
Action: ev.Rule.Action,
Duration: ev.Rule.Duration,
Operator: Operator{
RuleName: ev.Rule.Name,
Type: ev.Rule.Operator.Type,
Operand: ev.Rule.Operator.Operand,
Data: ev.Rule.Operator.Data,
},
},
}, opers...)

conns = append([]Connection{
Connection{
Node: addr,
Time: ev.Time,
Protocol: ev.Connection.Protocol,
SrcIP: ev.Connection.SrcIp,
SrcPort: ev.Connection.SrcPort,
DstIP: ev.Connection.DstIp,
DstHost: ev.Connection.DstHost,
DstPort: ev.Connection.DstPort,
UserID: ev.Connection.UserId,
PID: ev.Connection.ProcessId,
ProcessPath: ev.Connection.ProcessPath,
ProcessCwd: ev.Connection.ProcessCwd,
ProcessArgs: strings.Join(ev.Connection.ProcessArgs, " "),
//ProcessEnv: ev.Connection.ProcessEnv,
RuleName: ev.Rule.Name,
RuleAction: ev.Rule.Action,
},
}, conns...)
//fmt.Println("getEvents() ", ev)
}
return &opers, &conns
}

func (s *Storage) getProtoEventsByType(addr string, stats *protocol.Statistics) *[]EventByType {
var events []EventByType
for what, hits := range stats.ByProto {
events = append(events, []EventByType{
EventByType{
Time: time.Now(),
Node: addr, Name: "ByProto", What: what, Hits: hits},
}...)
}
for what, hits := range stats.ByAddress {
events = append(events, []EventByType{
EventByType{
Time: time.Now(),
Node: addr, Name: "ByAddress", What: what, Hits: hits},
}...)
}
for what, hits := range stats.ByHost {
events = append(events, []EventByType{
EventByType{
Time: time.Now(),
Node: addr, Name: "ByHost", What: what, Hits: hits},
}...)
}
for what, hits := range stats.ByPort {
events = append(events, []EventByType{
EventByType{
Time: time.Now(),
Node: addr, Name: "ByPort", What: what, Hits: hits},
}...)
}
for what, hits := range stats.ByUid {
events = append(events, []EventByType{
EventByType{
Time: time.Now(),
Node: addr, Name: "ByUid", What: what, Hits: hits},
}...)
}
for what, hits := range stats.ByExecutable {
events = append(events, []EventByType{
EventByType{
Time: time.Now(),
Node: addr, Name: "ByExecutable", What: what, Hits: hits},
}...)
}
return &events
}

func (s *Storage) getProtoRules(node string, nodeConf *protocol.ClientConfig) *[]Rule {
var rules []Rule
for _, pRule := range nodeConf.Rules {
rules = append(rules,
[]Rule{
Rule{
Node: node,
Name: pRule.Name,
Enabled: pRule.Enabled,
Action: pRule.Action,
Duration: pRule.Duration,
Operator: Operator{
Type: pRule.Operator.Type,
Operand: pRule.Operator.Operand,
Data: pRule.Operator.Data,
},
},
}...)
}

return &rules
}
Loading

0 comments on commit f1d406f

Please sign in to comment.