Skip to content

Commit

Permalink
allow to configure ebpf modules path
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gustavo-iniguez-goya committed Dec 22, 2023
1 parent 090bb0e commit ffb7668
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 63 deletions.
12 changes: 9 additions & 3 deletions daemon/core/ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"github.com/iovisor/gobpf/elf"
)

// LoadEbpfModule loads the given eBPF module
// It'll try to load from several paths.
func LoadEbpfModule(module string) (m *elf.Module, err error) {
// LoadEbpfModule loads the given eBPF module, from the given path if specified.
// Otherwise t'll try to load the module from several default paths.
func LoadEbpfModule(module, path string) (m *elf.Module, err error) {
var (
modulesDir = "/opensnitchd/ebpf"
paths = []string{
Expand All @@ -18,6 +18,12 @@ func LoadEbpfModule(module string) (m *elf.Module, err error) {
fmt.Sprint("/etc/opensnitchd"), // Deprecated: will be removed in future versions.
}
)

// if path has been specified, try to load the module from there.
if path != "" {
paths = []string{path}
}

modulePath := ""
moduleError := fmt.Errorf(`Module not found (%s) in any of the paths.
You may need to install the corresponding package`, module)
Expand Down
4 changes: 2 additions & 2 deletions daemon/dns/ebpfhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ func lookupSymbol(elffile *elf.File, symbolName string) (uint64, error) {
}

// ListenerEbpf starts listening for DNS events.
func ListenerEbpf() error {
m, err := core.LoadEbpfModule("opensnitch-dns.o")
func ListenerEbpf(ebpfModPath string) error {
m, err := core.LoadEbpfModule("opensnitch-dns.o", ebpfModPath)
if err != nil {
log.Error("[eBPF DNS]: %s", err)
return err
Expand Down
2 changes: 1 addition & 1 deletion daemon/firewall/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (c *Common) NewRulesChecker(areRulesLoaded callbackBool, reloadRules callba
}
}
c.stopChecker = make(chan bool, 1)
log.Info("Starting new fw checker every %s ...", DefaultCheckInterval)
log.Info("Starting new fw checker every %s ...", c.RulesCheckInterval)
c.RulesChecker = time.NewTicker(c.RulesCheckInterval)

go startCheckingRules(c.stopChecker, c.RulesChecker, areRulesLoaded, reloadRules)
Expand Down
12 changes: 7 additions & 5 deletions daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ var (
rulesPath = "/etc/opensnitchd/rules/"
configFile = "/etc/opensnitchd/default-config.json"
fwConfigFile = "/etc/opensnitchd/system-fw.json"
ebpfModPath = "" // /usr/lib/opensnitchd/ebpf
noLiveReload = false
queueNum = 0
repeatQueueNum int //will be set later to queueNum + 1
Expand Down Expand Up @@ -100,13 +101,14 @@ func init() {

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

flag.StringVar(&rulesPath, "rules-path", rulesPath, "Path to load JSON rules from.")
flag.StringVar(&configFile, "config-file", configFile, "Path to the daemon configuration file.")
flag.StringVar(&fwConfigFile, "fw-config-file", fwConfigFile, "Path to the system fw configuration file.")
//flag.StringVar(&ebpfModPath, "ebpf-modules-path", ebpfModPath, "Path to the directory with the eBPF modules.")
flag.StringVar(&logFile, "log-file", logFile, "Write logs to this file instead of the standard output.")
flag.BoolVar(&logUTC, "log-utc", logUTC, "Write logs output with UTC timezone (enabled by default).")
flag.BoolVar(&logMicro, "log-micro", logMicro, "Write logs output with microsecond timestamp (disabled by default).")
Expand Down Expand Up @@ -582,15 +584,15 @@ func main() {
// overwrite monitor method from configuration if the user has passed
// the option via command line.
if procmonMethod != "" {
if err := monitor.ReconfigureMonitorMethod(procmonMethod); err != nil {
if err := monitor.ReconfigureMonitorMethod(procmonMethod, cfg.Ebpf.ModulesPath); err != nil {
msg := fmt.Sprintf("Unable to set process monitor method via parameter: %v", err)
uiClient.SendWarningAlert(msg)
log.Warning(msg)
}
}

go func(uiClient *ui.Client) {
if err := dns.ListenerEbpf(); err != nil {
go func(uiClient *ui.Client, ebpfPath string) {
if err := dns.ListenerEbpf(ebpfPath); err != nil {
msg := fmt.Sprintf("EBPF-DNS: Unable to attach ebpf listener: %s", err)
log.Warning(msg)
// don't display an alert, since this module is not critical
Expand All @@ -602,7 +604,7 @@ func main() {
msg)

}
}(uiClient)
}(uiClient, ebpfModPath)

initSystemdResolvedMonitor()

Expand Down
16 changes: 10 additions & 6 deletions daemon/procmon/ebpf/ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ type Error struct {
}

var (
m, perfMod *elf.Module
lock = sync.RWMutex{}
mapSize = uint(12000)
ebpfMaps map[string]*ebpfMapsForProto
m, perfMod *elf.Module
lock = sync.RWMutex{}
mapSize = uint(12000)
ebpfMaps map[string]*ebpfMapsForProto
modulesPath string

//connections which were established at the time when opensnitch started
alreadyEstablished = alreadyEstablishedConns{
TCP: make(map[*daemonNetlink.Socket]int),
Expand All @@ -75,7 +77,9 @@ var (
)

//Start installs ebpf kprobes
func Start() *Error {
func Start(modPath string) *Error {
modulesPath = modPath

setRunning(false)
if err := mountDebugFS(); err != nil {
return &Error{
Expand All @@ -84,7 +88,7 @@ func Start() *Error {
}
}
var err error
m, err = core.LoadEbpfModule("opensnitch.o")
m, err = core.LoadEbpfModule("opensnitch.o", modulesPath)
if err != nil {
dispatchErrorEvent(fmt.Sprint("[eBPF]: ", err.Error()))
return &Error{NotAvailable, fmt.Errorf("[eBPF] Error loading opensnitch.o: %s", err.Error())}
Expand Down
2 changes: 1 addition & 1 deletion daemon/procmon/ebpf/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func initEventsStreamer() *Error {
elfOpts := make(map[string]elf.SectionParams)
elfOpts["maps/"+perfMapName] = elf.SectionParams{PerfRingBufferPageCount: ringBuffSize}
var err error
perfMod, err = core.LoadEbpfModule("opensnitch-procs.o")
perfMod, err = core.LoadEbpfModule("opensnitch-procs.o", modulesPath)
if err != nil {
dispatchErrorEvent(fmt.Sprint("[eBPF events]: ", err))
return &Error{EventsNotAvailable, err}
Expand Down
8 changes: 4 additions & 4 deletions daemon/procmon/monitor/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func stopProcMonitors() {
}

// ReconfigureMonitorMethod configures a new method for parsing connections.
func ReconfigureMonitorMethod(newMonitorMethod string) *Error {
func ReconfigureMonitorMethod(newMonitorMethod, ebpfModulesPath string) *Error {
if procmon.GetMonitorMethod() == newMonitorMethod {
return nil
}
Expand All @@ -64,7 +64,7 @@ func ReconfigureMonitorMethod(newMonitorMethod string) *Error {
// if the new monitor method fails to start, rollback the change and exit
// without saving the configuration. Otherwise we can end up with the wrong
// monitor method configured and saved to file.
err := Init()
err := Init(ebpfModulesPath)
if err.What > NoError {
log.Error("Reconf() -> Init() error: %v", err)
procmon.SetMonitorMethod(oldMethod)
Expand All @@ -85,7 +85,7 @@ func End() {
}

// Init starts parsing connections using the method specified.
func Init() (errm *Error) {
func Init(ebpfModulesPath string) (errm *Error) {
errm = &Error{}

if cacheMonitorsRunning == false {
Expand All @@ -94,7 +94,7 @@ func Init() (errm *Error) {
}

if procmon.MethodIsEbpf() {
err := ebpf.Start()
err := ebpf.Start(ebpfModulesPath)
if err == nil {
log.Info("Process monitor method ebpf")
return errm
Expand Down
85 changes: 46 additions & 39 deletions daemon/ui/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,60 +13,67 @@ import (
"github.com/evilsocket/opensnitch/daemon/statistics"
)

type serverTLSOptions struct {
CACert string `json:"CACert"`
ServerCert string `json:"ServerCert"`
ServerKey string `json:"ServerKey"`
ClientCert string `json:"ClientCert"`
ClientKey string `json:"ClientKey"`
// https://pkg.go.dev/crypto/tls#Config
SkipVerify bool `json:"SkipVerify"`
//https://pkg.go.dev/crypto/tls#ClientAuthType
ClientAuthType string `json:"ClientAuthType"`
type (
serverTLSOptions struct {
CACert string `json:"CACert"`
ServerCert string `json:"ServerCert"`
ServerKey string `json:"ServerKey"`
ClientCert string `json:"ClientCert"`
ClientKey string `json:"ClientKey"`
// https://pkg.go.dev/crypto/tls#Config
SkipVerify bool `json:"SkipVerify"`
// https://pkg.go.dev/crypto/tls#ClientAuthType
ClientAuthType string `json:"ClientAuthType"`

// https://pkg.go.dev/crypto/tls#Conn.VerifyHostname
//VerifyHostname bool
// https://pkg.go.dev/crypto/tls#example-Config-VerifyConnection
// VerifyConnection bool
// VerifyPeerCertificate bool
}
// https://pkg.go.dev/crypto/tls#Conn.VerifyHostname
// VerifyHostname bool
// https://pkg.go.dev/crypto/tls#example-Config-VerifyConnection
// VerifyConnection bool
// VerifyPeerCertificate bool
}

type serverAuth struct {
// token?, google?, simple-tls, mutual-tls
Type string `json:"Type"`
TLSOptions serverTLSOptions `json:"TLSOptions"`
}
serverAuth struct {
// token?, google?, simple-tls, mutual-tls
Type string `json:"Type"`
TLSOptions serverTLSOptions `json:"TLSOptions"`
}

type serverConfig struct {
Address string `json:"Address"`
Authentication serverAuth `json:"Authentication"`
LogFile string `json:"LogFile"`
Loggers []loggers.LoggerConfig `json:"Loggers"`
}
serverConfig struct {
Address string `json:"Address"`
Authentication serverAuth `json:"Authentication"`
LogFile string `json:"LogFile"`
Loggers []loggers.LoggerConfig `json:"Loggers"`
}

type rulesOptions struct {
Path string `json:"Path"`
EnableChecksums bool `json:"EnableChecksums"`
}
rulesOptions struct {
Path string `json:"Path"`
EnableChecksums bool `json:"EnableChecksums"`
}

type fwOptions struct {
Firewall string `json:"Firewall"`
ConfigPath string `json:"ConfigPath"`
ActionOnOverflow string `json:"ActionOnOverflow"`
MonitorInterval string `json:"MonitorInterval"`
}
fwOptions struct {
Firewall string `json:"Firewall"`
ConfigPath string `json:"ConfigPath"`
BypassQueue string `json:"BypassQueue"`
MonitorInterval string `json:"MonitorInterval"`
}

ebpfOptions struct {
ModulesPath string `json:"ModulesPath"`
}
)

// Config holds the values loaded from configFile
type Config struct {
sync.RWMutex
Server serverConfig `json:"Server"`
Stats statistics.StatsConfig `json:"Stats"`
Rules rulesOptions `json:"Rules"`
Ebpf ebpfOptions `json:"Ebpf"`
FwOptions fwOptions `json:"FwOptions"`
DefaultAction string `json:"DefaultAction"`
DefaultDuration string `json:"DefaultDuration"`
ProcMonitorMethod string `json:"ProcMonitorMethod"`
Rules rulesOptions `json:"Rules"`
Firewall string `json:"Firewall"`
FwOptions fwOptions `json:"FwOptions"`
LogLevel *int32 `json:"LogLevel"`
InterceptUnknown bool `json:"InterceptUnknown"`
LogUTC bool `json:"LogUTC"`
Expand Down
2 changes: 1 addition & 1 deletion daemon/ui/config_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (c *Client) loadConfiguration(rawConfig []byte) bool {
clientErrorRule.Duration = rule.Duration(clientConfig.DefaultDuration)
}
if clientConfig.ProcMonitorMethod != "" {
err := monitor.ReconfigureMonitorMethod(clientConfig.ProcMonitorMethod)
err := monitor.ReconfigureMonitorMethod(clientConfig.ProcMonitorMethod, clientConfig.Ebpf.ModulesPath)
if err != nil {
msg := fmt.Sprintf("Unable to set new process monitor (%s) method from disk: %v", clientConfig.ProcMonitorMethod, err.Msg)
log.Warning(msg)
Expand Down
5 changes: 4 additions & 1 deletion daemon/ui/notifications.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ func (c *Client) handleActionChangeConfig(stream protocol.UI_NotificationsClient
)
}

if err := monitor.ReconfigureMonitorMethod(newConf.ProcMonitorMethod); err != nil {
if err := monitor.ReconfigureMonitorMethod(
newConf.ProcMonitorMethod,
clientConfig.Ebpf.ModulesPath,
); err != nil {
c.sendNotificationReply(stream, notification.Id, "", err.Msg)
return
}
Expand Down

0 comments on commit ffb7668

Please sign in to comment.