2023-01-23 06:13:37 +00:00
/ * Copyright ( C ) 2018 Simone Margaritelli
// 2021 themighty1
// 2022 calesanz
// 2019-2022 Gustavo Iñiguez Goia
//
// This file is part of OpenSnitch.
//
// OpenSnitch is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenSnitch is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with OpenSnitch. If not, see <http://www.gnu.org/licenses/>.
* /
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"
2023-03-10 15:04:42 +01:00
"net"
2018-04-02 05:25:32 +02:00
"os"
"os/signal"
2018-04-16 17:51:54 +02:00
"runtime"
"runtime/pprof"
2024-01-17 21:31:55 +01:00
"runtime/trace"
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"
2023-03-10 15:04:42 +01:00
"github.com/evilsocket/opensnitch/daemon/dns/systemd"
2020-12-09 18:18:42 +01:00
"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"
2022-10-12 13:31:45 +02:00
"github.com/evilsocket/opensnitch/daemon/procmon/ebpf"
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"
2023-12-17 00:22:07 +01:00
"github.com/evilsocket/opensnitch/daemon/ui/config"
2022-10-12 13:31:45 +02:00
"github.com/evilsocket/opensnitch/daemon/ui/protocol"
2018-04-02 05:25:32 +02:00
)
var (
2023-05-18 14:23:40 +02:00
showVersion = false
checkRequirements = false
procmonMethod = ""
logFile = ""
2023-06-05 20:46:42 -06:00
logUTC = true
logMicro = false
2023-12-17 00:22:07 +01:00
rulesPath = "/etc/opensnitchd/rules/"
configFile = "/etc/opensnitchd/default-config.json"
2023-12-20 21:32:45 +01:00
fwConfigFile = "/etc/opensnitchd/system-fw.json"
2023-12-22 23:27:18 +01:00
ebpfModPath = "" // /usr/lib/opensnitchd/ebpf
2023-05-18 14:23:40 +02:00
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 = ""
2024-01-17 21:31:55 +01:00
traceFile = ""
memFile * os . File
2018-04-16 17:51:54 +02:00
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 )
2024-01-17 21:31:55 +01:00
repeatQueue = ( * netfilter . Queue ) ( nil )
2021-02-18 17:21:50 +03:00
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
2023-03-10 15:04:42 +01:00
resolvMonitor * systemd . ResolvedMonitor
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." )
2023-05-18 14:23:40 +02:00
flag . BoolVar ( & checkRequirements , "check-requirements" , debug , "Check system requirements for incompatibilities." )
2021-03-22 17:51:31 +01:00
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 . 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
2023-12-22 23:27:18 +01:00
flag . StringVar ( & rulesPath , "rules-path" , rulesPath , "Path to load JSON rules from." )
2023-12-17 00:22:07 +01:00
flag . StringVar ( & configFile , "config-file" , configFile , "Path to the daemon configuration file." )
2023-12-20 21:32:45 +01:00
flag . StringVar ( & fwConfigFile , "fw-config-file" , fwConfigFile , "Path to the system fw configuration file." )
2023-12-22 23:27:18 +01:00
//flag.StringVar(&ebpfModPath, "ebpf-modules-path", ebpfModPath, "Path to the directory with the eBPF modules.")
2018-04-05 17:41:12 +02:00
flag . StringVar ( & logFile , "log-file" , logFile , "Write logs to this file instead of the standard output." )
2023-06-05 20:46:42 -06:00
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)." )
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." )
2024-01-17 21:31:55 +01:00
flag . StringVar ( & traceFile , "trace-file" , traceFile , "Write trace file to this file." )
2018-04-02 05:25:32 +02:00
}
2023-12-17 00:22:07 +01:00
// Load configuration file from disk, by default from /etc/opensnitchd/default-config.json,
// or from the path specified by configFile.
// This configuration will be loaded again by uiClient(), in order to monitor it for changes.
func loadDiskConfiguration ( ) ( * config . Config , error ) {
if configFile == "" {
return nil , fmt . Errorf ( "Configuration file cannot be empty" )
}
raw , err := config . Load ( configFile )
if err != nil || len ( raw ) == 0 {
return nil , fmt . Errorf ( "Error loading configuration %s: %s" , configFile , err )
}
clientConfig , err := config . Parse ( raw )
if err != nil {
return nil , fmt . Errorf ( "Error parsing configuration %s: %s" , configFile , err )
}
log . Info ( "Loading configuration file %s ..." , configFile )
return & clientConfig , nil
}
2020-11-02 01:37:35 +01:00
func overwriteLogging ( ) bool {
2023-06-09 17:43:32 +02:00
return debug || warning || important || errorlog || logFile != "" || logMicro
2020-11-02 01:37:35 +01:00
}
2024-05-14 23:41:25 +02:00
func setupQueues ( qNum uint16 ) {
2024-01-18 11:37:07 +01:00
// prepare the queue
var err error
2024-05-14 23:41:25 +02:00
queue , err = netfilter . NewQueue ( qNum )
2024-01-18 11:37:07 +01:00
if err != nil {
2024-05-14 23:41:25 +02:00
msg := fmt . Sprintf ( "Error creating queue #%d: %s" , qNum , err )
2024-01-18 11:37:07 +01:00
uiClient . SendWarningAlert ( msg )
log . Warning ( "Is opensnitchd already running?" )
log . Fatal ( msg )
}
pktChan = queue . Packets ( )
2024-05-14 23:41:25 +02:00
repeatQueueNum = int ( qNum ) + 1
2024-01-18 11:37:07 +01:00
repeatQueue , err = netfilter . NewQueue ( uint16 ( repeatQueueNum ) )
if err != nil {
msg := fmt . Sprintf ( "Error creating repeat queue #%d: %s" , repeatQueueNum , err )
uiClient . SendErrorAlert ( msg )
log . Warning ( "Is opensnitchd already running?" )
log . Warning ( msg )
}
repeatPktChan = repeatQueue . Packets ( )
2024-05-14 23:41:25 +02:00
log . Info ( "Listening on queue number %d ..." , qNum )
2024-01-18 11:37:07 +01:00
}
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
}
2023-06-05 20:46:42 -06:00
log . SetLogUTC ( logUTC )
log . SetLogMicro ( logMicro )
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 ( ) {
2024-01-17 21:31:55 +01:00
if traceFile != "" {
log . Info ( "setup trace profile: %s" , traceFile )
f , err := os . Create ( traceFile )
if err != nil {
log . Fatal ( "could not create trace profile: %s" , err )
}
trace . Start ( f )
}
if memProfile != "" {
log . Info ( "setup mem profile: %s" , memProfile )
var err error
memFile , err = os . Create ( memProfile )
if err != nil {
log . Fatal ( "could not create memory profile: %s" , err )
}
}
2022-05-25 17:46:27 +02:00
if cpuProfile != "" {
2024-01-17 21:31:55 +01:00
log . Info ( "setup cpu profile: %s" , cpuProfile )
2022-05-25 17:46:27 +02:00
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 ( )
2022-06-24 01:09:45 +02:00
time . AfterFunc ( 10 * time . Second , func ( ) {
log . Error ( "[REVIEW] closing due to timeout" )
os . Exit ( 0 )
} )
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 )
}
}
2022-10-12 13:31:45 +02:00
// Listen to events sent from other modules
func listenToEvents ( ) {
for i := 0 ; i < 5 ; i ++ {
go func ( uiClient * ui . Client ) {
for evt := range ebpf . Events ( ) {
// for loop vars are per-loop, not per-item
evt := evt
uiClient . PostAlert (
protocol . Alert_WARNING ,
protocol . Alert_KERNEL_EVENT ,
protocol . Alert_SHOW_ALERT ,
protocol . Alert_MEDIUM ,
evt )
}
} ( uiClient )
}
}
2023-03-10 15:04:42 +01:00
func initSystemdResolvedMonitor ( ) {
resolvMonitor , err := systemd . NewResolvedMonitor ( )
if err != nil {
2023-05-28 16:23:39 +02:00
log . Debug ( "[DNS] Unable to use systemd-resolved monitor: %s" , err )
2023-03-10 15:04:42 +01:00
return
}
_ , err = resolvMonitor . Connect ( )
if err != nil {
2023-05-28 16:23:39 +02:00
log . Debug ( "[DNS] Connecting to systemd-resolved: %s" , err )
2023-03-10 15:04:42 +01:00
return
}
err = resolvMonitor . Subscribe ( )
if err != nil {
2023-05-28 16:23:39 +02:00
log . Debug ( "[DNS] Subscribing to systemd-resolved DNS events: %s" , err )
2023-03-10 15:04:42 +01:00
return
}
go func ( ) {
2024-01-18 11:37:07 +01:00
var ip net . IP
2023-03-10 15:04:42 +01:00
for {
select {
case exit := <- resolvMonitor . Exit ( ) :
if exit == nil {
2023-05-28 16:23:39 +02:00
log . Info ( "[DNS] systemd-resolved monitor stopped" )
2023-03-10 15:04:42 +01:00
return
}
2023-05-28 16:23:39 +02:00
log . Debug ( "[DNS] systemd-resolved monitor disconnected. Reconnecting..." )
2023-03-10 15:04:42 +01:00
case response := <- resolvMonitor . GetDNSResponses ( ) :
if response . State != systemd . SuccessState {
2023-05-28 16:23:39 +02:00
log . Debug ( "[DNS] systemd-resolved monitor response error: %v" , response )
2023-03-10 15:04:42 +01:00
continue
}
/ * for i , q := range response . Question {
log . Debug ( "%d SYSTEMD RESPONSE Q: %s" , i , q . Name )
} * /
for i , a := range response . Answer {
2023-03-12 21:37:11 +01:00
if a . RR . Key . Type != systemd . DNSTypeA &&
a . RR . Key . Type != systemd . DNSTypeAAAA &&
a . RR . Key . Type != systemd . DNSTypeCNAME {
log . Debug ( "systemd-resolved, excluding answer: %#v" , a )
continue
}
2024-01-18 11:37:07 +01:00
ip = net . IP ( a . RR . Address )
2024-01-25 16:00:49 +01:00
log . Debug ( "%d systemd-resolved monitor response: %s -> %s" , i , a . RR . Key . Name , ip )
2023-03-12 21:37:11 +01:00
if a . RR . Key . Type == systemd . DNSTypeCNAME {
2024-01-18 11:37:07 +01:00
log . Debug ( "systemd-resolved CNAME >> %s -> %s" , a . RR . Name , a . RR . Key . Name )
dns . Track ( a . RR . Name , a . RR . Key . Name /*domain*/ )
2023-03-10 15:04:42 +01:00
} else {
2024-01-18 11:37:07 +01:00
dns . Track ( ip . String ( ) , a . RR . Key . Name /*domain*/ )
2023-03-10 15:04:42 +01:00
}
}
}
}
} ( )
}
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 ( )
2023-03-10 15:04:42 +01:00
if resolvMonitor != nil {
resolvMonitor . Close ( )
}
2018-04-17 18:08:03 +02:00
2018-04-16 17:51:54 +02:00
if cpuProfile != "" {
pprof . StopCPUProfile ( )
}
if memProfile != "" {
runtime . GC ( ) // get up-to-date statistics
2024-01-17 21:31:55 +01:00
if err := pprof . WriteHeapProfile ( memFile ) ; err != nil {
log . Error ( "Could not write memory profile: %s" , err )
2018-04-16 17:51:54 +02:00
}
2024-01-17 21:31:55 +01:00
log . Info ( "Writing mem profile to %s" , memProfile )
memFile . Close ( )
}
if traceFile != "" {
trace . Stop ( )
2018-04-16 17:51:54 +02:00
}
2024-01-17 21:31:55 +01:00
repeatQueue . Close ( )
queue . Close ( )
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 {
2024-05-01 00:23:48 +02:00
applyDefaultAction ( & packet , nil )
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 )
2022-07-06 16:26:28 +02:00
if r != nil && r . Nolog {
return
}
2023-03-10 21:30:28 +01:00
// XXX: if a connection is not intercepted due to InterceptUnknown == false,
// it's not sent to the server, which leads to miss information.
2020-03-03 23:51:25 +01:00
stats . OnConnectionEvent ( con , r , r == nil )
}
2024-05-01 00:23:48 +02:00
func applyDefaultAction ( packet * netfilter . Packet , con * conman . Connection ) {
2020-05-10 17:08:08 +02:00
if uiClient . DefaultAction ( ) == rule . Allow {
2021-01-25 11:33:24 +03:00
packet . SetVerdictAndMark ( netfilter . NF_ACCEPT , packet . Mark )
2024-05-01 00:23:48 +02:00
return
}
if uiClient . DefaultAction ( ) == rule . Reject && con != nil {
netlink . KillSocket ( con . Protocol , con . SrcIP , con . SrcPort , con . DstIP , con . DstPort )
2020-05-10 17:08:08 +02:00
}
2024-05-01 00:23:48 +02: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 {
2024-05-01 00:23:48 +02:00
applyDefaultAction ( packet , con )
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
2023-03-10 15:04:42 +01:00
if con . DstHost == "" {
con . DstHost = dns . HostOr ( con . DstIP , con . DstHost )
}
2022-02-15 21:25:35 +01:00
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" )
2024-05-01 00:23:48 +02:00
applyDefaultAction ( packet , con )
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 {
2024-05-01 00:23:48 +02:00
applyDefaultAction ( packet , con )
2020-05-10 17:08:08 +02:00
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 )
}
2023-04-21 23:28:13 +02:00
log . Debug ( "%s %s -> %d:%s => %s:%d, mark: %x (%s)" , log . Bold ( log . Green ( "✔" ) ) , log . Bold ( con . Process . Path ) , con . SrcPort , log . Bold ( con . SrcIP . String ( ) ) , log . Bold ( con . To ( ) ) , con . DstPort , packet . Mark , 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
2023-04-21 23:28:13 +02:00
log . Debug ( "%s %s -> %d:%s => %s:%d, mark: %x (%s)" , log . Bold ( log . Red ( "✘" ) ) , log . Bold ( con . Process . Path ) , con . SrcPort , log . Bold ( con . SrcIP . String ( ) ) , log . Bold ( con . To ( ) ) , con . DstPort , packet . Mark , 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 )
}
2023-05-18 14:23:40 +02:00
if checkRequirements {
core . CheckSysRequirements ( )
os . Exit ( 0 )
}
2021-03-22 17:51:31 +01:00
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
2023-12-17 00:22:07 +01:00
cfg , err := loadDiskConfiguration ( )
if err != nil {
log . Fatal ( "%s" , err )
}
2024-01-18 11:37:07 +01:00
2023-12-17 00:22:07 +01:00
if err == nil && cfg . Rules . Path != "" {
rulesPath = cfg . Rules . Path
}
if rulesPath == "" {
log . Fatal ( "rules path cannot be empty" )
}
2018-04-02 05:34:21 +02:00
rulesPath , err := core . ExpandPath ( rulesPath )
if err != nil {
2022-10-12 13:31:45 +02:00
log . Fatal ( "Error accessing rules path (does it exist?): %s" , err )
2018-04-02 05:34:21 +02:00
}
2024-05-12 00:59:48 +02:00
if cfg . FwOptions . ConfigPath == "" {
cfg . FwOptions . ConfigPath = fwConfigFile
}
log . Info ( "Using system fw configuration %s ..." , fwConfigFile )
2024-05-14 23:41:25 +02:00
if uint16 ( queueNum ) != cfg . FwOptions . QueueNum && queueNum > 0 {
cfg . FwOptions . QueueNum = uint16 ( queueNum )
}
2018-04-02 05:34:21 +02:00
setupSignals ( )
2018-04-08 15:32:20 +02:00
log . Info ( "Loading rules from %s ..." , rulesPath )
2023-12-17 00:22:07 +01:00
rules , err = rule . NewLoader ( ! noLiveReload )
if err != nil {
2018-04-08 16:36:12 +02:00
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 ( )
2024-05-14 19:47:35 +02:00
stats . SetLoggers ( loggerMgr )
2023-12-17 00:22:07 +01:00
uiClient = ui . NewClient ( uiSocket , configFile , stats , rules , loggerMgr )
2018-04-02 05:25:32 +02:00
2018-04-08 15:32:20 +02:00
setupWorkers ( )
2024-05-14 23:41:25 +02:00
setupQueues ( cfg . FwOptions . QueueNum )
2021-02-18 17:21:50 +03:00
2024-05-12 00:59:48 +02:00
// queue and firewall rules should be ready by now
2021-04-17 22:09:16 +02:00
2022-12-16 17:03:36 +01:00
uiClient . Connect ( )
listenToEvents ( )
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 != "" {
2023-12-22 23:27:18 +01:00
if err := monitor . ReconfigureMonitorMethod ( procmonMethod , cfg . Ebpf . ModulesPath ) ; err != nil {
2022-10-12 13:31:45 +02:00
msg := fmt . Sprintf ( "Unable to set process monitor method via parameter: %v" , err )
uiClient . SendWarningAlert ( msg )
log . Warning ( msg )
2021-09-04 23:09:04 +02:00
}
2020-03-16 01:37:33 +01:00
}
2019-10-21 19:23:29 +02:00
2023-12-22 23:27:18 +01:00
go func ( uiClient * ui . Client , ebpfPath string ) {
if err := dns . ListenerEbpf ( ebpfPath ) ; err != nil {
2022-10-12 13:31:45 +02:00
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
uiClient . PostAlert (
protocol . Alert_ERROR ,
protocol . Alert_GENERIC ,
protocol . Alert_SAVE_TO_DB ,
protocol . Alert_MEDIUM ,
msg )
2022-02-15 21:25:35 +01:00
}
2024-01-04 01:12:03 +01:00
} ( uiClient , cfg . Ebpf . ModulesPath )
2022-02-15 21:25:35 +01:00
2023-03-10 15:04:42 +01:00
initSystemdResolvedMonitor ( )
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
}