2018-04-02 05:25:32 +02:00
package main
import (
2021-02-18 17:21:50 +03:00
"bytes"
2020-07-17 01:29:58 +02:00
"context"
2018-04-02 05:25:32 +02:00
"flag"
2018-04-16 17:51:54 +02:00
"fmt"
2018-04-02 05:25:32 +02:00
"io/ioutil"
golog "log"
"os"
"os/signal"
2018-04-16 17:51:54 +02:00
"runtime"
"runtime/pprof"
2018-04-02 05:25:32 +02:00
"syscall"
2021-02-18 17:21:50 +03:00
"time"
2018-04-02 05:25:32 +02:00
2020-12-09 18:18:42 +01:00
"github.com/evilsocket/opensnitch/daemon/conman"
"github.com/evilsocket/opensnitch/daemon/core"
"github.com/evilsocket/opensnitch/daemon/dns"
"github.com/evilsocket/opensnitch/daemon/firewall"
"github.com/evilsocket/opensnitch/daemon/log"
2022-05-17 16:15:40 +02:00
"github.com/evilsocket/opensnitch/daemon/log/loggers"
2020-12-09 18:18:42 +01:00
"github.com/evilsocket/opensnitch/daemon/netfilter"
2021-09-12 10:54:24 +02:00
"github.com/evilsocket/opensnitch/daemon/netlink"
2021-04-05 09:28:16 +00:00
"github.com/evilsocket/opensnitch/daemon/procmon/monitor"
2020-12-09 18:18:42 +01:00
"github.com/evilsocket/opensnitch/daemon/rule"
"github.com/evilsocket/opensnitch/daemon/statistics"
"github.com/evilsocket/opensnitch/daemon/ui"
2018-04-02 05:25:32 +02:00
)
var (
2021-03-22 17:51:31 +01:00
showVersion = false
2021-02-18 17:21:50 +03:00
procmonMethod = ""
logFile = ""
rulesPath = "rules"
noLiveReload = false
queueNum = 0
repeatQueueNum int //will be set later to queueNum + 1
workers = 16
debug = false
warning = false
important = false
errorlog = false
2018-04-02 05:25:32 +02:00
2020-10-26 23:16:27 +01:00
uiSocket = ""
2018-04-07 01:52:43 +02:00
uiClient = ( * ui . Client ) ( nil )
2018-04-02 05:25:32 +02:00
2018-04-16 17:51:54 +02:00
cpuProfile = ""
memProfile = ""
2021-02-18 17:21:50 +03:00
ctx = ( context . Context ) ( nil )
cancel = ( context . CancelFunc ) ( nil )
err = ( error ) ( nil )
rules = ( * rule . Loader ) ( nil )
stats = ( * statistics . Statistics ) ( nil )
queue = ( * netfilter . Queue ) ( nil )
repeatPktChan = ( <- chan netfilter . Packet ) ( nil )
pktChan = ( <- chan netfilter . Packet ) ( nil )
wrkChan = ( chan netfilter . Packet ) ( nil )
sigChan = ( chan os . Signal ) ( nil )
exitChan = ( chan bool ) ( nil )
2022-05-25 17:46:27 +02:00
loggerMgr * loggers . LoggerManager
2018-04-02 05:25:32 +02:00
)
func init ( ) {
2021-03-22 17:51:31 +01:00
flag . BoolVar ( & showVersion , "version" , debug , "Show daemon version of this executable and exit." )
2021-04-05 09:28:16 +00:00
flag . StringVar ( & procmonMethod , "process-monitor-method" , procmonMethod , "How to search for processes path. Options: ftrace, audit (experimental), ebpf (experimental), proc (default)" )
2018-04-07 01:52:43 +02:00
flag . StringVar ( & uiSocket , "ui-socket" , uiSocket , "Path the UI gRPC service listener (https://github.com/grpc/grpc/blob/master/doc/naming.md)." )
2018-04-02 05:25:32 +02:00
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." )
2018-04-08 16:36:12 +02:00
flag . BoolVar ( & noLiveReload , "no-live-reload" , debug , "Disable rules live reloading." )
2018-04-05 17:41:12 +02:00
flag . StringVar ( & logFile , "log-file" , logFile , "Write logs to this file instead of the standard output." )
2020-11-02 01:37:35 +01:00
flag . BoolVar ( & debug , "debug" , debug , "Enable debug level logs." )
flag . BoolVar ( & warning , "warning" , warning , "Enable warning level logs." )
2020-02-28 10:24:08 +01:00
flag . BoolVar ( & important , "important" , important , "Enable important level logs." )
flag . BoolVar ( & errorlog , "error" , errorlog , "Enable error level logs." )
2018-04-16 17:51:54 +02:00
flag . StringVar ( & cpuProfile , "cpu-profile" , cpuProfile , "Write CPU profile to this file." )
flag . StringVar ( & memProfile , "mem-profile" , memProfile , "Write memory profile to this file." )
2018-04-02 05:25:32 +02:00
}
2020-11-02 01:37:35 +01:00
func overwriteLogging ( ) bool {
return debug || warning || important || errorlog || logFile != ""
}
2018-04-08 16:36:12 +02:00
func setupLogging ( ) {
golog . SetOutput ( ioutil . Discard )
if debug {
2020-06-14 20:14:24 +02:00
log . SetLogLevel ( log . DEBUG )
2020-02-28 10:24:08 +01:00
} else if warning {
2020-06-14 20:14:24 +02:00
log . SetLogLevel ( log . WARNING )
2020-02-28 10:24:08 +01:00
} else if important {
2020-06-14 20:14:24 +02:00
log . SetLogLevel ( log . IMPORTANT )
2020-02-28 10:24:08 +01:00
} else if errorlog {
2020-06-14 20:14:24 +02:00
log . SetLogLevel ( log . ERROR )
2018-04-08 16:36:12 +02:00
} else {
2020-06-14 20:14:24 +02:00
log . SetLogLevel ( log . INFO )
2018-04-08 16:36:12 +02:00
}
2021-02-16 21:19:54 +03:00
var logFileToUse string
if logFile == "" {
logFileToUse = log . StdoutFile
} else {
logFileToUse = logFile
}
log . Close ( )
if err := log . OpenFile ( logFileToUse ) ; err != nil {
log . Error ( "Error opening user defined log: %s %s" , logFileToUse , err )
2018-04-08 16:36:12 +02:00
}
}
2022-05-25 17:46:27 +02:00
func setupProfiling ( ) {
if cpuProfile != "" {
if f , err := os . Create ( cpuProfile ) ; err != nil {
log . Fatal ( "%s" , err )
} else if err := pprof . StartCPUProfile ( f ) ; err != nil {
log . Fatal ( "%s" , err )
}
}
}
2018-04-02 05:25:32 +02:00
func setupSignals ( ) {
sigChan = make ( chan os . Signal , 1 )
2020-07-17 01:29:58 +02:00
exitChan = make ( chan bool , workers + 1 )
2018-04-02 05:25:32 +02:00
signal . Notify ( sigChan ,
syscall . SIGHUP ,
syscall . SIGINT ,
syscall . SIGTERM ,
syscall . SIGQUIT )
go func ( ) {
sig := <- sigChan
log . Raw ( "\n" )
log . Important ( "Got signal: %v" , sig )
2020-07-17 01:29:58 +02:00
cancel ( )
2018-04-02 05:25:32 +02:00
} ( )
}
func worker ( id int ) {
log . Debug ( "Worker #%d started." , id )
for true {
select {
2020-07-17 01:29:58 +02:00
case <- ctx . Done ( ) :
goto Exit
default :
pkt , ok := <- wrkChan
if ! ok {
2021-02-13 19:16:25 +03:00
log . Debug ( "worker channel closed %d" , id )
2020-07-17 01:29:58 +02:00
goto Exit
}
2018-04-02 05:25:32 +02:00
onPacket ( pkt )
}
}
2020-07-17 01:29:58 +02:00
Exit :
log . Debug ( "worker #%d exit" , id )
2018-04-02 05:25:32 +02:00
}
func setupWorkers ( ) {
2018-04-02 05:34:21 +02:00
log . Debug ( "Starting %d workers ..." , workers )
2018-04-02 05:25:32 +02:00
// setup the workers
2018-04-10 13:06:02 +02:00
wrkChan = make ( chan netfilter . Packet )
2018-04-02 05:25:32 +02:00
for i := 0 ; i < workers ; i ++ {
go worker ( i )
}
}
2021-02-18 17:21:50 +03:00
func doCleanup ( queue , repeatQueue * netfilter . Queue ) {
2018-04-02 05:25:32 +02:00
log . Info ( "Cleaning up ..." )
2021-06-07 01:32:05 +02:00
firewall . Stop ( )
2021-04-05 09:28:16 +00:00
monitor . End ( )
2020-07-17 01:29:58 +02:00
uiClient . Close ( )
queue . Close ( )
2021-02-18 17:21:50 +03:00
repeatQueue . Close ( )
2018-04-17 18:08:03 +02:00
2018-04-16 17:51:54 +02:00
if cpuProfile != "" {
pprof . StopCPUProfile ( )
}
if memProfile != "" {
f , err := os . Create ( memProfile )
if err != nil {
fmt . Printf ( "Could not create memory profile: %s\n" , err )
return
}
defer f . Close ( )
runtime . GC ( ) // get up-to-date statistics
if err := pprof . WriteHeapProfile ( f ) ; err != nil {
fmt . Printf ( "Could not write memory profile: %s\n" , err )
}
}
2018-04-02 05:25:32 +02:00
}
2018-04-10 13:06:02 +02:00
func onPacket ( packet netfilter . Packet ) {
2018-04-02 05:25:32 +02:00
// DNS response, just parse, track and accept.
if dns . TrackAnswers ( packet . Packet ) == true {
2021-01-25 11:33:24 +03:00
packet . SetVerdictAndMark ( netfilter . NF_ACCEPT , packet . Mark )
2018-04-05 23:56:07 +02:00
stats . OnDNSResponse ( )
2018-04-02 05:25:32 +02:00
return
}
// Parse the connection state
2019-11-01 01:00:10 +01:00
con := conman . Parse ( packet , uiClient . InterceptUnknown ( ) )
2018-04-02 05:25:32 +02:00
if con == nil {
2020-05-10 17:08:08 +02:00
applyDefaultAction ( & packet )
2018-04-02 05:25:32 +02:00
return
}
2020-04-19 20:13:31 +02:00
// accept our own connections
if con . Process . ID == os . Getpid ( ) {
packet . SetVerdict ( netfilter . NF_ACCEPT )
return
}
2018-04-02 05:25:32 +02:00
2018-04-02 18:26:04 +02:00
// search a match in preloaded rules
2020-03-03 23:51:25 +01:00
r := acceptOrDeny ( & packet , con )
stats . OnConnectionEvent ( con , r , r == nil )
}
2020-05-10 17:08:08 +02:00
func applyDefaultAction ( packet * netfilter . Packet ) {
if uiClient . DefaultAction ( ) == rule . Allow {
2021-01-25 11:33:24 +03:00
packet . SetVerdictAndMark ( netfilter . NF_ACCEPT , packet . Mark )
2020-05-10 17:08:08 +02:00
} else {
2021-02-19 09:25:08 +03:00
packet . SetVerdict ( netfilter . NF_DROP )
2020-05-10 17:08:08 +02:00
}
}
2020-03-03 23:51:25 +01:00
func acceptOrDeny ( packet * netfilter . Packet , con * conman . Connection ) * rule . Rule {
2018-04-02 05:25:32 +02:00
r := rules . FindFirstMatch ( con )
if r == nil {
2021-02-18 17:21:50 +03:00
// no rule matched
// Note that as soon as we set a verdict on a packet, the next packet in the netfilter queue
// will begin to be processed even if this function hasn't yet returned
// send a request to the UI client if
// 1) connected and running and 2) we are not already asking
if uiClient . Connected ( ) == false || uiClient . GetIsAsking ( ) == true {
applyDefaultAction ( packet )
2021-04-17 22:09:16 +02:00
log . Debug ( "UI is not running or busy, connected: %v, running: %v" , uiClient . Connected ( ) , uiClient . GetIsAsking ( ) )
2021-02-18 17:21:50 +03:00
return nil
}
uiClient . SetIsAsking ( true )
defer uiClient . SetIsAsking ( false )
// In order not to block packet processing, we send our packet to a different netfilter queue
// and then immediately pull it back out of that queue
packet . SetRequeueVerdict ( uint16 ( repeatQueueNum ) )
var o bool
var pkt netfilter . Packet
// don't wait for the packet longer than 1 sec
select {
case pkt , o = <- repeatPktChan :
if ! o {
log . Debug ( "error while receiving packet from repeatPktChan" )
return nil
}
case <- time . After ( 1 * time . Second ) :
log . Debug ( "timed out while receiving packet from repeatPktChan" )
return nil
}
//check if the pulled out packet is the same we put in
if res := bytes . Compare ( packet . Packet . Data ( ) , pkt . Packet . Data ( ) ) ; res != 0 {
2021-08-11 14:00:23 +02:00
log . Error ( "The packet which was requeued has changed abruptly. This should never happen. Please report this incident to the Opensnitch developers. %v %v " , packet , pkt )
2021-02-18 17:21:50 +03:00
return nil
}
packet = & pkt
2022-02-15 21:25:35 +01:00
// Update the hostname again.
// This is required due to a race between the ebpf dns hook and the actual first packet beeing sent
con . DstHost = dns . HostOr ( con . DstIP , con . DstHost )
2021-02-18 17:21:50 +03:00
r = uiClient . Ask ( con )
2020-02-14 23:15:14 +01:00
if r == nil {
2020-06-04 00:38:11 +02:00
log . Error ( "Invalid rule received, applying default action" )
applyDefaultAction ( packet )
2020-03-03 23:51:25 +01:00
return nil
2020-02-14 23:15:14 +01:00
}
2021-02-18 17:21:50 +03:00
ok := false
pers := ""
action := string ( r . Action )
if r . Action == rule . Allow {
action = log . Green ( action )
} else {
action = log . Red ( action )
}
// check if and how the rule needs to be saved
if r . Duration == rule . Always {
pers = "Saved"
// add to the loaded rules and persist on disk
if err := rules . Add ( r , true ) ; err != nil {
log . Error ( "Error while saving rule: %s" , err )
2018-04-05 18:15:36 +02:00
} else {
2021-02-18 17:21:50 +03:00
ok = true
2018-04-05 18:15:36 +02:00
}
2021-02-18 17:21:50 +03:00
} else {
pers = "Added"
// add to the rules but do not save to disk
if err := rules . Add ( r , false ) ; err != nil {
log . Error ( "Error while adding rule: %s" , err )
2019-11-09 01:35:13 +01:00
} else {
2021-02-18 17:21:50 +03:00
ok = true
2018-04-03 14:51:58 +02:00
}
2021-02-18 17:21:50 +03:00
}
2018-04-05 18:15:36 +02:00
2021-02-18 17:21:50 +03:00
if ok {
log . Important ( "%s new rule: %s if %s" , pers , action , r . Operator . String ( ) )
2018-04-03 14:51:58 +02:00
}
2021-02-18 17:21:50 +03:00
2018-04-02 05:25:32 +02:00
}
2021-09-12 10:54:24 +02:00
if packet == nil {
log . Debug ( "Packet nil after processing rules" )
return r
}
2018-04-02 05:25:32 +02:00
2020-05-10 17:08:08 +02:00
if r . Enabled == false {
applyDefaultAction ( packet )
ruleName := log . Green ( r . Name )
log . Info ( "DISABLED (%s) %s %s -> %s:%d (%s)" , uiClient . DefaultAction ( ) , log . Bold ( log . Green ( "✔" ) ) , log . Bold ( con . Process . Path ) , log . Bold ( con . To ( ) ) , con . DstPort , ruleName )
} else if r . Action == rule . Allow {
2021-09-12 10:54:24 +02:00
packet . SetVerdictAndMark ( netfilter . NF_ACCEPT , packet . Mark )
2018-04-02 05:25:32 +02:00
ruleName := log . Green ( r . Name )
2018-04-07 13:52:25 +02:00
if r . Operator . Operand == rule . OpTrue {
2018-04-02 05:25:32 +02:00
ruleName = log . Dim ( r . Name )
}
2018-04-03 14:51:58 +02:00
log . Debug ( "%s %s -> %s:%d (%s)" , log . Bold ( log . Green ( "✔" ) ) , log . Bold ( con . Process . Path ) , log . Bold ( con . To ( ) ) , con . DstPort , ruleName )
2018-04-08 20:13:35 +02:00
} else {
2021-09-12 10:54:24 +02:00
if r . Action == rule . Reject {
netlink . KillSocket ( con . Protocol , con . SrcIP , con . SrcPort , con . DstIP , con . DstPort )
2020-03-03 23:51:25 +01:00
}
2021-09-12 10:54:24 +02:00
packet . SetVerdict ( netfilter . NF_DROP )
2018-04-02 05:25:32 +02:00
2020-04-20 01:00:16 +02:00
log . Debug ( "%s %s -> %s:%d (%s)" , log . Bold ( log . Red ( "✘" ) ) , log . Bold ( con . Process . Path ) , log . Bold ( con . To ( ) ) , con . DstPort , log . Red ( r . Name ) )
2018-04-08 20:13:35 +02:00
}
2020-03-03 23:51:25 +01:00
return r
2018-04-02 05:25:32 +02:00
}
func main ( ) {
2020-07-17 01:29:58 +02:00
ctx , cancel = context . WithCancel ( context . Background ( ) )
defer cancel ( )
2018-04-02 05:25:32 +02:00
flag . Parse ( )
2021-03-22 17:51:31 +01:00
if showVersion {
fmt . Println ( core . Version )
os . Exit ( 0 )
}
2018-04-08 16:36:12 +02:00
setupLogging ( )
2022-05-25 17:46:27 +02:00
setupProfiling ( )
2018-04-16 17:51:54 +02:00
2018-04-02 05:34:21 +02:00
log . Important ( "Starting %s v%s" , core . Name , core . Version )
2018-04-02 05:25:32 +02:00
2018-04-02 05:34:21 +02:00
rulesPath , err := core . ExpandPath ( rulesPath )
if err != nil {
log . Fatal ( "%s" , err )
}
setupSignals ( )
2018-04-08 15:32:20 +02:00
log . Info ( "Loading rules from %s ..." , rulesPath )
2018-04-08 16:36:12 +02:00
if rules , err = rule . NewLoader ( ! noLiveReload ) ; err != nil {
log . Fatal ( "%s" , err )
} else if err = rules . Load ( rulesPath ) ; err != nil {
2018-04-08 15:32:20 +02:00
log . Fatal ( "%s" , err )
}
2022-05-25 17:46:27 +02:00
stats = statistics . New ( rules )
loggerMgr = loggers . NewLoggerManager ( )
uiClient = ui . NewClient ( uiSocket , stats , rules , loggerMgr )
2018-04-02 05:25:32 +02:00
// prepare the queue
2018-04-08 15:32:20 +02:00
setupWorkers ( )
2018-04-10 13:15:30 +02:00
queue , err := netfilter . NewQueue ( uint16 ( queueNum ) )
2018-04-02 05:25:32 +02:00
if err != nil {
2021-02-18 17:21:50 +03:00
log . Warning ( "Is opensnitchd already running?" )
2018-04-02 05:25:32 +02:00
log . Fatal ( "Error while creating queue #%d: %s" , queueNum , err )
}
2018-04-10 12:52:01 +02:00
pktChan = queue . Packets ( )
2018-04-02 05:25:32 +02:00
2021-02-18 17:21:50 +03:00
repeatQueueNum = queueNum + 1
repeatQueue , rqerr := netfilter . NewQueue ( uint16 ( repeatQueueNum ) )
if rqerr != nil {
log . Warning ( "Is opensnitchd already running?" )
log . Fatal ( "Error while creating queue #%d: %s" , repeatQueueNum , rqerr )
}
repeatPktChan = repeatQueue . Packets ( )
2021-04-17 22:09:16 +02:00
// queue is ready, run firewall rules
2021-08-09 00:21:43 +02:00
firewall . Init ( uiClient . GetFirewallType ( ) , & queueNum )
2021-04-17 22:09:16 +02:00
2020-11-02 01:37:35 +01:00
if overwriteLogging ( ) {
setupLogging ( )
}
2020-04-19 20:13:31 +02:00
// overwrite monitor method from configuration if the user has passed
// the option via command line.
2020-03-16 01:37:33 +01:00
if procmonMethod != "" {
2021-09-04 23:09:04 +02:00
if err := monitor . ReconfigureMonitorMethod ( procmonMethod ) ; err != nil {
log . Warning ( "Unable to set process monitor method via parameter: %v" , err )
}
2020-03-16 01:37:33 +01:00
}
2019-10-21 19:23:29 +02:00
2022-02-15 21:25:35 +01:00
go func ( ) {
2022-06-10 17:01:18 +02:00
err := dns . ListenerEbpf ( )
2022-02-15 21:25:35 +01:00
if err != nil {
log . Warning ( "EBPF-DNS: Unable to attach ebpf listener." )
}
} ( )
2018-04-02 05:25:32 +02:00
log . Info ( "Running on netfilter queue #%d ..." , queueNum )
2020-07-17 01:29:58 +02:00
for {
2018-04-02 05:25:32 +02:00
select {
2020-07-17 01:29:58 +02:00
case <- ctx . Done ( ) :
goto Exit
case pkt , ok := <- pktChan :
if ! ok {
goto Exit
}
2018-04-02 05:25:32 +02:00
wrkChan <- pkt
}
}
2020-07-17 01:29:58 +02:00
Exit :
close ( wrkChan )
2021-02-18 17:21:50 +03:00
doCleanup ( queue , repeatQueue )
2020-07-17 01:29:58 +02:00
os . Exit ( 0 )
2018-04-02 05:25:32 +02:00
}