Skip to content

Commit 5c6da0a

Browse files
allow to configure ebpf modules path
Now it's possible to configure eBPF modules path from the default-config.json file: "Ebpf": { "ModulesPath": "..." } If the option is not provided, or if it's empty, we'll keep loading from the default directories: - /usr/local/lib/opensnitchd/ebpf - /usr/lib/opensnitchd/ebpf - /etc/opensnitchd/ebpf (deprecated, will be removed in the future). Closes #928 (cherry picked from commit ffb7668)
1 parent 946799b commit 5c6da0a

File tree

9 files changed

+142
-71
lines changed

9 files changed

+142
-71
lines changed

daemon/core/ebpf.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import (
77
"github.com/iovisor/gobpf/elf"
88
)
99

10-
// LoadEbpfModule loads the given eBPF module
11-
// It'll try to load from several paths.
12-
func LoadEbpfModule(module string) (m *elf.Module, err error) {
10+
// LoadEbpfModule loads the given eBPF module, from the given path if specified.
11+
// Otherwise t'll try to load the module from several default paths.
12+
func LoadEbpfModule(module, path string) (m *elf.Module, err error) {
1313
var (
1414
modulesDir = "/opensnitchd/ebpf"
1515
paths = []string{
@@ -18,6 +18,12 @@ func LoadEbpfModule(module string) (m *elf.Module, err error) {
1818
fmt.Sprint("/etc/opensnitchd"), // Deprecated: will be removed in future versions.
1919
}
2020
)
21+
22+
// if path has been specified, try to load the module from there.
23+
if path != "" {
24+
paths = []string{path}
25+
}
26+
2127
modulePath := ""
2228
moduleError := fmt.Errorf(`Module not found (%s) in any of the paths.
2329
You may need to install the corresponding package`, module)

daemon/dns/ebpfhook.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ func lookupSymbol(elffile *elf.File, symbolName string) (uint64, error) {
9393
}
9494

9595
// ListenerEbpf starts listening for DNS events.
96-
func ListenerEbpf() error {
97-
m, err := core.LoadEbpfModule("opensnitch-dns.o")
96+
func ListenerEbpf(ebpfModPath string) error {
97+
m, err := core.LoadEbpfModule("opensnitch-dns.o", ebpfModPath)
9898
if err != nil {
9999
log.Error("[eBPF DNS]: %s", err)
100100
return err

daemon/main.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ var (
6363
logMicro = false
6464
rulesPath = "/etc/opensnitchd/rules/"
6565
configFile = "/etc/opensnitchd/default-config.json"
66+
ebpfModPath = "" // /usr/lib/opensnitchd/ebpf
6667
noLiveReload = false
6768
queueNum = 0
6869
repeatQueueNum int //will be set later to queueNum + 1
@@ -99,12 +100,13 @@ func init() {
99100

100101
flag.StringVar(&procmonMethod, "process-monitor-method", procmonMethod, "How to search for processes path. Options: ftrace, audit (experimental), ebpf (experimental), proc (default)")
101102
flag.StringVar(&uiSocket, "ui-socket", uiSocket, "Path the UI gRPC service listener (https://github.com/grpc/grpc/blob/master/doc/naming.md).")
102-
flag.StringVar(&rulesPath, "rules-path", rulesPath, "Path to load JSON rules from.")
103103
flag.IntVar(&queueNum, "queue-num", queueNum, "Netfilter queue number.")
104104
flag.IntVar(&workers, "workers", workers, "Number of concurrent workers.")
105105
flag.BoolVar(&noLiveReload, "no-live-reload", debug, "Disable rules live reloading.")
106106

107+
flag.StringVar(&rulesPath, "rules-path", rulesPath, "Path to load JSON rules from.")
107108
flag.StringVar(&configFile, "config-file", configFile, "Path to the daemon configuration file.")
109+
//flag.StringVar(&ebpfModPath, "ebpf-modules-path", ebpfModPath, "Path to the directory with the eBPF modules.")
108110
flag.StringVar(&logFile, "log-file", logFile, "Write logs to this file instead of the standard output.")
109111
flag.BoolVar(&logUTC, "log-utc", logUTC, "Write logs output with UTC timezone (enabled by default).")
110112
flag.BoolVar(&logMicro, "log-micro", logMicro, "Write logs output with microsecond timestamp (disabled by default).")
@@ -571,15 +573,15 @@ func main() {
571573
// overwrite monitor method from configuration if the user has passed
572574
// the option via command line.
573575
if procmonMethod != "" {
574-
if err := monitor.ReconfigureMonitorMethod(procmonMethod); err != nil {
576+
if err := monitor.ReconfigureMonitorMethod(procmonMethod, cfg.Ebpf.ModulesPath); err != nil {
575577
msg := fmt.Sprintf("Unable to set process monitor method via parameter: %v", err)
576578
uiClient.SendWarningAlert(msg)
577579
log.Warning(msg)
578580
}
579581
}
580582

581-
go func(uiClient *ui.Client) {
582-
if err := dns.ListenerEbpf(); err != nil {
583+
go func(uiClient *ui.Client, ebpfPath string) {
584+
if err := dns.ListenerEbpf(ebpfPath); err != nil {
583585
msg := fmt.Sprintf("EBPF-DNS: Unable to attach ebpf listener: %s", err)
584586
log.Warning(msg)
585587
// don't display an alert, since this module is not critical
@@ -591,7 +593,7 @@ func main() {
591593
msg)
592594

593595
}
594-
}(uiClient)
596+
}(uiClient, ebpfModPath)
595597

596598
initSystemdResolvedMonitor()
597599

daemon/procmon/ebpf/ebpf.go

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,26 @@ type alreadyEstablishedConns struct {
3838
sync.RWMutex
3939
}
4040

41+
// list of returned errors
42+
const (
43+
NoError = iota
44+
NotAvailable
45+
EventsNotAvailable
46+
)
47+
48+
// Error returns the error type and a message with the explanation
49+
type Error struct {
50+
What int // 1 global error, 2 events error, 3 ...
51+
Msg error
52+
}
53+
4154
var (
42-
m, perfMod *elf.Module
43-
lock = sync.RWMutex{}
44-
mapSize = uint(12000)
45-
ebpfMaps map[string]*ebpfMapsForProto
55+
m, perfMod *elf.Module
56+
lock = sync.RWMutex{}
57+
mapSize = uint(12000)
58+
ebpfMaps map[string]*ebpfMapsForProto
59+
modulesPath string
60+
4661
//connections which were established at the time when opensnitch started
4762
alreadyEstablished = alreadyEstablishedConns{
4863
TCP: make(map[*daemonNetlink.Socket]int),
@@ -62,18 +77,24 @@ var (
6277
)
6378

6479
//Start installs ebpf kprobes
65-
func Start() error {
80+
func Start(modPath string) *Error {
81+
modulesPath = modPath
82+
6683
setRunning(false)
6784
if err := mountDebugFS(); err != nil {
6885
log.Error("ebpf.Start -> mount debugfs error. Report on github please: %s", err)
69-
return err
86+
return &Error{
87+
NotAvailable,
88+
fmt.Errorf("ebpf.Start: mount debugfs error. Report on github please: %s", err),
89+
}
90+
7091
}
7192
var err error
72-
m, err = core.LoadEbpfModule("opensnitch.o")
93+
m, err = core.LoadEbpfModule("opensnitch.o", modulesPath)
7394
if err != nil {
7495
log.Error("%s", err)
7596
dispatchErrorEvent(fmt.Sprint("[eBPF]: ", err.Error()))
76-
return err
97+
return &Error{NotAvailable, fmt.Errorf("[eBPF] Error loading opensnitch.o: %s", err.Error())}
7798
}
7899
m.EnableOptionCompatProbe()
79100

@@ -83,12 +104,10 @@ func Start() error {
83104
if err := m.EnableKprobes(0); err != nil {
84105
m.Close()
85106
if err := m.Load(nil); err != nil {
86-
log.Error("eBPF failed to load /etc/opensnitchd/opensnitch.o (2): %v", err)
87-
return err
107+
return &Error{NotAvailable, fmt.Errorf("eBPF failed to load /etc/opensnitchd/opensnitch.o (2): %v", err)}
88108
}
89109
if err := m.EnableKprobes(0); err != nil {
90-
log.Error("eBPF error when enabling kprobes: %v", err)
91-
return err
110+
return &Error{NotAvailable, fmt.Errorf("eBPF error when enabling kprobes: %v", err)}
92111
}
93112
}
94113
determineHostByteOrder()
@@ -105,7 +124,7 @@ func Start() error {
105124
}
106125
for prot, mfp := range ebpfMaps {
107126
if mfp.bpfmap == nil {
108-
return fmt.Errorf("eBPF module opensnitch.o malformed, bpfmap[%s] nil", prot)
127+
return &Error{NotAvailable, fmt.Errorf("eBPF module opensnitch.o malformed, bpfmap[%s] nil", prot)}
109128
}
110129
}
111130

daemon/procmon/ebpf/events.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func initEventsStreamer() {
7575
elfOpts := make(map[string]elf.SectionParams)
7676
elfOpts["maps/"+perfMapName] = elf.SectionParams{PerfRingBufferPageCount: ringBuffSize}
7777
var err error
78-
perfMod, err = core.LoadEbpfModule("opensnitch-procs.o")
78+
perfMod, err = core.LoadEbpfModule("opensnitch-procs.o", modulesPath)
7979
if err != nil {
8080
dispatchErrorEvent(fmt.Sprint("[eBPF events]: ", err))
8181
return

daemon/procmon/monitor/init.go

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package monitor
22

33
import (
4-
"net"
5-
64
"github.com/evilsocket/opensnitch/daemon/log"
75
"github.com/evilsocket/opensnitch/daemon/procmon"
86
"github.com/evilsocket/opensnitch/daemon/procmon/audit"
@@ -13,20 +11,39 @@ var (
1311
cacheMonitorsRunning = false
1412
)
1513

16-
// ReconfigureMonitorMethod configures a new method for parsing connections.
17-
func ReconfigureMonitorMethod(newMonitorMethod string) error {
14+
// List of errors that this package may return.
15+
const (
16+
NoError = iota
17+
ProcFsErr
18+
AuditdErr
19+
EbpfErr
20+
EbpfEventsErr
21+
)
1822

23+
// Error wraps the type of error with its message
24+
type Error struct {
25+
What int
26+
Msg error
27+
}
28+
29+
// ReconfigureMonitorMethod configures a new method for parsing connections.
30+
func ReconfigureMonitorMethod(newMonitorMethod, ebpfModulesPath string) *Error {
1931
if procmon.GetMonitorMethod() == newMonitorMethod {
2032
return nil
2133
}
2234

2335
oldMethod := procmon.GetMonitorMethod()
36+
if oldMethod == "" {
37+
oldMethod = procmon.MethodProc
38+
}
2439
End()
2540
procmon.SetMonitorMethod(newMonitorMethod)
2641
// if the new monitor method fails to start, rollback the change and exit
2742
// without saving the configuration. Otherwise we can end up with the wrong
2843
// monitor method configured and saved to file.
29-
if err := Init(); err != nil {
44+
err := Init(ebpfModulesPath)
45+
if err.What > NoError {
46+
log.Error("Reconf() -> Init() error: %v", err)
3047
procmon.SetMonitorMethod(oldMethod)
3148
return err
3249
}
@@ -44,36 +61,52 @@ func End() {
4461
}
4562

4663
// Init starts parsing connections using the method specified.
47-
func Init() (err error) {
64+
func Init(ebpfModulesPath string) (errm *Error) {
65+
errm = &Error{}
66+
4867
if cacheMonitorsRunning == false {
4968
go procmon.MonitorActivePids()
5069
go procmon.CacheCleanerTask()
5170
cacheMonitorsRunning = true
5271
}
5372

5473
if procmon.MethodIsEbpf() {
55-
err = ebpf.Start()
74+
err := ebpf.Start(ebpfModulesPath)
5675
if err == nil {
5776
log.Info("Process monitor method ebpf")
58-
return nil
77+
return errm
5978
}
79+
// ebpf main module loaded, we can use ebpf
80+
81+
// XXX: this will have to be rewritten when we'll have more events (bind, listen, etc)
82+
if err.What == ebpf.EventsNotAvailable {
83+
log.Info("Process monitor method ebpf")
84+
log.Warning("opensnitch-procs.o not available: %s", err.Msg)
85+
86+
return errm
87+
}
88+
6089
// we need to stop this method even if it has failed to start, in order to clean up the kprobes
6190
// It helps with the error "cannot write...kprobe_events: file exists".
6291
ebpf.Stop()
92+
errm.What = err.What
93+
errm.Msg = err.Msg
6394
log.Warning("error starting ebpf monitor method: %v", err)
95+
6496
} else if procmon.MethodIsAudit() {
65-
var auditConn net.Conn
66-
auditConn, err = audit.Start()
97+
auditConn, err := audit.Start()
6798
if err == nil {
6899
log.Info("Process monitor method audit")
69100
go audit.Reader(auditConn, (chan<- audit.Event)(audit.EventChan))
70-
return nil
101+
return &Error{AuditdErr, err}
71102
}
103+
errm.What = AuditdErr
104+
errm.Msg = err
72105
log.Warning("error starting audit monitor method: %v", err)
73106
}
74107

75108
// if any of the above methods have failed, fallback to proc
76109
log.Info("Process monitor method /proc")
77110
procmon.SetMonitorMethod(procmon.MethodProc)
78-
return err
111+
return errm
79112
}

daemon/ui/config/config.go

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,52 +13,59 @@ import (
1313
"github.com/evilsocket/opensnitch/daemon/statistics"
1414
)
1515

16-
type serverTLSOptions struct {
17-
CACert string `json:"CACert"`
18-
ServerCert string `json:"ServerCert"`
19-
ServerKey string `json:"ServerKey"`
20-
ClientCert string `json:"ClientCert"`
21-
ClientKey string `json:"ClientKey"`
22-
// https://pkg.go.dev/crypto/tls#Config
23-
SkipVerify bool `json:"SkipVerify"`
24-
//https://pkg.go.dev/crypto/tls#ClientAuthType
25-
ClientAuthType string `json:"ClientAuthType"`
16+
type (
17+
serverTLSOptions struct {
18+
CACert string `json:"CACert"`
19+
ServerCert string `json:"ServerCert"`
20+
ServerKey string `json:"ServerKey"`
21+
ClientCert string `json:"ClientCert"`
22+
ClientKey string `json:"ClientKey"`
23+
// https://pkg.go.dev/crypto/tls#Config
24+
SkipVerify bool `json:"SkipVerify"`
25+
// https://pkg.go.dev/crypto/tls#ClientAuthType
26+
ClientAuthType string `json:"ClientAuthType"`
2627

27-
// https://pkg.go.dev/crypto/tls#Conn.VerifyHostname
28-
//VerifyHostname bool
29-
// https://pkg.go.dev/crypto/tls#example-Config-VerifyConnection
30-
// VerifyConnection bool
31-
// VerifyPeerCertificate bool
32-
}
28+
// https://pkg.go.dev/crypto/tls#Conn.VerifyHostname
29+
// VerifyHostname bool
30+
// https://pkg.go.dev/crypto/tls#example-Config-VerifyConnection
31+
// VerifyConnection bool
32+
// VerifyPeerCertificate bool
33+
}
3334

34-
type serverAuth struct {
35-
// token?, google?, simple-tls, mutual-tls
36-
Type string `json:"Type"`
37-
TLSOptions serverTLSOptions `json:"TLSOptions"`
38-
}
35+
serverAuth struct {
36+
// token?, google?, simple-tls, mutual-tls
37+
Type string `json:"Type"`
38+
TLSOptions serverTLSOptions `json:"TLSOptions"`
39+
}
3940

40-
type serverConfig struct {
41-
Address string `json:"Address"`
42-
Authentication serverAuth `json:"Authentication"`
43-
LogFile string `json:"LogFile"`
44-
Loggers []loggers.LoggerConfig `json:"Loggers"`
45-
}
41+
serverConfig struct {
42+
Address string `json:"Address"`
43+
Authentication serverAuth `json:"Authentication"`
44+
LogFile string `json:"LogFile"`
45+
Loggers []loggers.LoggerConfig `json:"Loggers"`
46+
}
4647

47-
type rulesOptions struct {
48-
Path string `json:"Path"`
49-
}
48+
rulesOptions struct {
49+
Path string `json:"Path"`
50+
}
51+
52+
ebpfOptions struct {
53+
ModulesPath string `json:"ModulesPath"`
54+
}
55+
)
5056

5157
// Config holds the values loaded from configFile
5258
type Config struct {
5359
sync.RWMutex
5460
Server serverConfig `json:"Server"`
5561
Stats statistics.StatsConfig `json:"Stats"`
5662
Rules rulesOptions `json:"Rules"`
63+
Ebpf ebpfOptions `json:"Ebpf"`
5764
DefaultAction string `json:"DefaultAction"`
5865
DefaultDuration string `json:"DefaultDuration"`
5966
ProcMonitorMethod string `json:"ProcMonitorMethod"`
6067
Firewall string `json:"Firewall"`
61-
LogLevel *uint32 `json:"LogLevel"`
68+
LogLevel *int32 `json:"LogLevel"`
6269
InterceptUnknown bool `json:"InterceptUnknown"`
6370
LogUTC bool `json:"LogUTC"`
6471
LogMicro bool `json:"LogMicro"`

daemon/ui/config_utils.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ func (c *Client) loadConfiguration(rawConfig []byte) bool {
109109
clientErrorRule.Duration = rule.Duration(clientConfig.DefaultDuration)
110110
}
111111
if clientConfig.ProcMonitorMethod != "" {
112-
if err := monitor.ReconfigureMonitorMethod(clientConfig.ProcMonitorMethod); err != nil {
113-
msg := fmt.Sprintf("Unable to set new process monitor (%s) method from disk: %v", clientConfig.ProcMonitorMethod, err)
112+
err := monitor.ReconfigureMonitorMethod(clientConfig.ProcMonitorMethod, clientConfig.Ebpf.ModulesPath)
113+
if err != nil {
114+
msg := fmt.Sprintf("Unable to set new process monitor (%s) method from disk: %v", clientConfig.ProcMonitorMethod, err.Msg)
114115
log.Warning(msg)
115116
c.SendWarningAlert(msg)
116117
}

0 commit comments

Comments
 (0)