Start firewall/network/portmaster adaption

This commit is contained in:
Daniel 2018-10-30 19:14:13 +01:00
parent 97a46d1e57
commit 99851166a0
11 changed files with 250 additions and 212 deletions

24
firewall/config.go Normal file
View file

@ -0,0 +1,24 @@
package firewall
import (
"github.com/Safing/portbase/config"
)
var (
permanentVerdicts config.BoolOption
)
func prep() error {
err := config.Register(&config.Option{
Name: "Permanent Verdicts",
Key: "firewall/permanentVerdicts",
Description: "With permanent verdicts, control of a connection is fully handed back to the OS after the initial decision. This brings a great performance increase, but makes it impossible to change the decision of a link later on.",
ExpertiseLevel: config.ExpertiseLevelExpert,
OptType: config.OptTypeBool,
DefaultValue: true,
})
if err != nil {
return err
}
configuredNameServers = config.Concurrent.GetAsBool("firewall/permanentVerdicts", true)
}

View file

@ -8,17 +8,15 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/Safing/safing-core/configuration" "github.com/Safing/portbase/config"
"github.com/Safing/safing-core/firewall/inspection" "github.com/Safing/portbase/log"
"github.com/Safing/safing-core/firewall/interception" "github.com/Safing/portbase/modules"
"github.com/Safing/safing-core/log" "github.com/Safing/portmaster/firewall/inspection"
"github.com/Safing/safing-core/modules" "github.com/Safing/portmaster/firewall/interception"
"github.com/Safing/safing-core/network" "github.com/Safing/portmaster/network"
"github.com/Safing/safing-core/network/packet" "github.com/Safing/portmaster/network/packet"
"github.com/Safing/safing-core/port17/entry" "github.com/Safing/portmaster/portmaster"
"github.com/Safing/safing-core/port17/mode" "github.com/Safing/portmaster/process"
"github.com/Safing/safing-core/portmaster"
"github.com/Safing/safing-core/process"
) )
var ( var (
@ -46,23 +44,25 @@ var (
) )
func init() { func init() {
modules.Register("firewall", prep, start, stop, "database", "nameserver")
}
var err error func prep() (err error) {
_, localNet4, err = net.ParseCIDR("127.0.0.0/24") _, localNet4, err = net.ParseCIDR("127.0.0.0/24")
// Yes, this would normally be 127.0.0.0/8 // Yes, this would normally be 127.0.0.0/8
// TODO: figure out any side effects // TODO: figure out any side effects
if err != nil { if err != nil {
log.Criticalf("firewall: failed to parse cidr 127.0.0.0/24: %s", err) return fmt.Errorf("firewall: failed to parse cidr 127.0.0.0/24: %s", err)
} }
_, tunnelNet4, err = net.ParseCIDR("127.17.0.0/16") _, tunnelNet4, err = net.ParseCIDR("127.17.0.0/16")
if err != nil { if err != nil {
log.Criticalf("firewall: failed to parse cidr 127.17.0.0/16: %s", err) return fmt.Errorf("firewall: failed to parse cidr 127.17.0.0/16: %s", err)
} }
_, tunnelNet6, err = net.ParseCIDR("fd17::/64") _, tunnelNet6, err = net.ParseCIDR("fd17::/64")
if err != nil { if err != nil {
log.Criticalf("firewall: failed to parse cidr fd17::/64: %s", err) return fmt.Errorf("firewall: failed to parse cidr fd17::/64: %s", err)
} }
var pA uint64 var pA uint64
@ -71,20 +71,19 @@ func init() {
packetsBlocked = &pB packetsBlocked = &pB
var pD uint64 var pD uint64
packetsDropped = &pD packetsDropped = &pD
return nil
} }
func Start() { func start() {
firewallModule = modules.Register("Firewall", 128)
defer firewallModule.StopComplete()
// start interceptor // start interceptor
go interception.Start() interception.Start()
go statLogger()
go statLogger()
go run()
// go run() // go run()
// go run() // go run()
// go run() // go run()
run()
} }
func handlePacket(pkt packet.Packet) { func handlePacket(pkt packet.Packet) {
@ -189,19 +188,19 @@ func initialHandler(pkt packet.Packet, link *network.Link) {
logInitialVerdict(link) logInitialVerdict(link)
// TODO: link this to real status // TODO: link this to real status
port17Active := mode.Client() // port17Active := mode.Client()
switch { switch {
case port17Active && link.Inspect: // case port17Active && link.Inspect:
// tunnel link, but also inspect (after reroute) // // tunnel link, but also inspect (after reroute)
link.Tunneled = true // link.Tunneled = true
link.SetFirewallHandler(inspectThenVerdict) // link.SetFirewallHandler(inspectThenVerdict)
verdict(pkt, link.Verdict) // verdict(pkt, link.Verdict)
case port17Active: // case port17Active:
// tunnel link, don't inspect // // tunnel link, don't inspect
link.Tunneled = true // link.Tunneled = true
link.StopFirewallHandler() // link.StopFirewallHandler()
permanentVerdict(pkt, network.ACCEPT) // permanentVerdict(pkt, network.ACCEPT)
case link.Inspect: case link.Inspect:
link.SetFirewallHandler(inspectThenVerdict) link.SetFirewallHandler(inspectThenVerdict)
inspectThenVerdict(pkt, link) inspectThenVerdict(pkt, link)
@ -227,11 +226,7 @@ func inspectThenVerdict(pkt packet.Packet, link *network.Link) {
// we are done with inspecting // we are done with inspecting
link.StopFirewallHandler() link.StopFirewallHandler()
config.Changed() link.VerdictPermanent = permanentVerdicts()
config.RLock()
link.VerdictPermanent = config.PermanentVerdicts
config.RUnlock()
if link.VerdictPermanent { if link.VerdictPermanent {
link.Save() link.Save()
permanentVerdict(pkt, link.Verdict) permanentVerdict(pkt, link.Verdict)
@ -276,18 +271,18 @@ func verdict(pkt packet.Packet, action network.Verdict) {
pkt.Drop() pkt.Drop()
} }
func tunnelHandler(pkt packet.Packet) { // func tunnelHandler(pkt packet.Packet) {
tunnelInfo := portmaster.GetTunnelInfo(pkt.GetIPHeader().Dst) // tunnelInfo := portmaster.GetTunnelInfo(pkt.GetIPHeader().Dst)
if tunnelInfo == nil { // if tunnelInfo == nil {
pkt.Block() // pkt.Block()
return // return
} // }
//
entry.CreateTunnel(pkt, tunnelInfo.Domain, tunnelInfo.RRCache.ExportAllARecords()) // entry.CreateTunnel(pkt, tunnelInfo.Domain, tunnelInfo.RRCache.ExportAllARecords())
log.Tracef("firewall: rerouting %s to tunnel entry point", pkt) // log.Tracef("firewall: rerouting %s to tunnel entry point", pkt)
pkt.RerouteToTunnel() // pkt.RerouteToTunnel()
return // return
} // }
func logInitialVerdict(link *network.Link) { func logInitialVerdict(link *network.Link) {
// switch link.Verdict { // switch link.Verdict {
@ -312,25 +307,26 @@ func logChangedVerdict(link *network.Link) {
} }
func run() { func run() {
packetProcessingLoop:
for { for {
select { select {
case <-firewallModule.Stop: case <-modules.ShuttingDown():
break packetProcessingLoop return
case pkt := <-interception.Packets: case pkt := <-interception.Packets:
handlePacket(pkt) handlePacket(pkt)
} }
} }
} }
func statLogger() { func statLogger() {
for { for {
time.Sleep(10 * time.Second) select {
log.Tracef("firewall: packets accepted %d, blocked %d, dropped %d", atomic.LoadUint64(packetsAccepted), atomic.LoadUint64(packetsBlocked), atomic.LoadUint64(packetsDropped)) case <-modules.ShuttingDown():
atomic.StoreUint64(packetsAccepted, 0) return
atomic.StoreUint64(packetsBlocked, 0) case <-time.After(10 * time.Second):
atomic.StoreUint64(packetsDropped, 0) log.Tracef("firewall: packets accepted %d, blocked %d, dropped %d", atomic.LoadUint64(packetsAccepted), atomic.LoadUint64(packetsBlocked), atomic.LoadUint64(packetsDropped))
atomic.StoreUint64(packetsAccepted, 0)
atomic.StoreUint64(packetsBlocked, 0)
atomic.StoreUint64(packetsDropped, 0)
}
} }
} }

View file

@ -3,9 +3,10 @@
package inspection package inspection
import ( import (
"github.com/Safing/safing-core/network"
"github.com/Safing/safing-core/network/packet"
"sync" "sync"
"github.com/Safing/portmaster/network"
"github.com/Safing/portmaster/network/packet"
) )
const ( const (

View file

@ -2,10 +2,8 @@
package interception package interception
import "github.com/Safing/safing-core/network/packet" import "github.com/Safing/portmaster/network/packet"
var Packets chan packet.Packet var (
func init() {
Packets = make(chan packet.Packet, 1000) Packets = make(chan packet.Packet, 1000)
} )

View file

@ -5,27 +5,33 @@
package interception package interception
import ( import (
"fmt"
"sort" "sort"
"strings" "strings"
"github.com/coreos/go-iptables/iptables" "github.com/coreos/go-iptables/iptables"
"github.com/Safing/safing-core/firewall/interception/nfqueue" "github.com/Safing/portmaster/firewall/interception/nfqueue"
"github.com/Safing/safing-core/log"
"github.com/Safing/safing-core/modules"
) )
// iptables -A OUTPUT -p icmp -j", "NFQUEUE", "--queue-num", "1", "--queue-bypass // iptables -A OUTPUT -p icmp -j", "NFQUEUE", "--queue-num", "1", "--queue-bypass
var nfqueueModule *modules.Module var (
v4chains []string
v4rules []string
v4once []string
var v4chains []string v6chains []string
var v4rules []string v6rules []string
var v4once []string v6once []string
var v6chains []string out4Queue *nfqueue.NFQueue
var v6rules []string in4Queue *nfqueue.NFQueue
var v6once []string out6Queue *nfqueue.NFQueue
in6Queue *nfqueue.NFQueue
shutdownSignal = make(chan struct{})
)
func init() { func init() {
@ -238,70 +244,84 @@ func deactivateNfqueueFirewall() error {
return nil return nil
} }
func Start() { // Start starts the nfqueue interception.
func Start() (err error) {
nfqueueModule = modules.Register("Firewall:Interception:Nfqueue", 192) err = activateNfqueueFirewall()
if err != nil {
if err := activateNfqueueFirewall(); err != nil { Stop()
log.Criticalf("could not activate firewall for nfqueue: %q", err) return fmt.Errorf("could not initialize nfqueue: %s", err)
} }
out4Queue := nfqueue.NewNFQueue(17040) out4Queue, err = nfqueue.NewNFQueue(17040)
in4Queue := nfqueue.NewNFQueue(17140) if err != nil {
out6Queue := nfqueue.NewNFQueue(17060) Stop()
in6Queue := nfqueue.NewNFQueue(17160) return fmt.Errorf("interception: failed to create nfqueue(IPv4, in): %s", err)
}
in4Queue, err = nfqueue.NewNFQueue(17140)
if err != nil {
Stop()
return fmt.Errorf("interception: failed to create nfqueue(IPv4, in): %s", err)
}
out6Queue, err = nfqueue.NewNFQueue(17060)
if err != nil {
Stop()
return fmt.Errorf("interception: failed to create nfqueue(IPv4, in): %s", err)
}
in6Queue, err = nfqueue.NewNFQueue(17160)
if err != nil {
Stop()
return fmt.Errorf("interception: failed to create nfqueue(IPv4, in): %s", err)
}
out4Channel := out4Queue.Process() go handleInterception()
// if err != nil { return nil
// log.Criticalf("could not open nfqueue out4") }
// } else {
defer out4Queue.Destroy()
// }
in4Channel := in4Queue.Process()
// if err != nil {
// log.Criticalf("could not open nfqueue in4")
// } else {
defer in4Queue.Destroy()
// }
out6Channel := out6Queue.Process()
// if err != nil {
// log.Criticalf("could not open nfqueue out6")
// } else {
defer out6Queue.Destroy()
// }
in6Channel := in6Queue.Process()
// if err != nil {
// log.Criticalf("could not open nfqueue in6")
// } else {
defer in6Queue.Destroy()
// }
packetInterceptionLoop: // Stop stops the nfqueue interception.
func Stop() error {
defer close(shutdownSignal)
if out4Queue != nil {
out4Queue.Destroy()
}
if in4Queue != nil {
in4Queue.Destroy()
}
if out6Queue != nil {
out6Queue.Destroy()
}
if in6Queue != nil {
in6Queue.Destroy()
}
err := deactivateNfqueueFirewall()
if err != nil {
return fmt.Errorf("interception: error while deactivating nfqueue: %s", err)
}
return nil
}
func handleInterception() {
for { for {
select { select {
case <-nfqueueModule.Stop: case <-shutdownSignal:
break packetInterceptionLoop return
case pkt := <-out4Channel: case pkt := <-out4Queue.Packets:
pkt.SetOutbound() pkt.SetOutbound()
Packets <- pkt Packets <- pkt
case pkt := <-in4Channel: case pkt := <-in4Queue.Packets:
pkt.SetInbound() pkt.SetInbound()
Packets <- pkt Packets <- pkt
case pkt := <-out6Channel: case pkt := <-out6Queue.Packets:
pkt.SetOutbound() pkt.SetOutbound()
Packets <- pkt Packets <- pkt
case pkt := <-in6Channel: case pkt := <-in6Queue.Packets:
pkt.SetInbound() pkt.SetInbound()
Packets <- pkt Packets <- pkt
} }
} }
if err := deactivateNfqueueFirewall(); err != nil {
log.Criticalf("could not deactivate firewall for nfqueue: %q", err)
}
nfqueueModule.StopComplete()
} }
func stringInSlice(slice []string, value string) bool { func stringInSlice(slice []string, value string) bool {

View file

@ -2,45 +2,48 @@
package nfqueue package nfqueue
import ( // suspended for now
"github.com/Safing/safing-core/network/packet"
"sync"
)
type multiQueue struct { // import (
qs []*nfQueue // "sync"
} //
// "github.com/Safing/portmaster/network/packet"
func NewMultiQueue(min, max uint16) (mq *multiQueue) { // )
mq = &multiQueue{make([]*nfQueue, 0, max-min)} //
for i := min; i < max; i++ { // type multiQueue struct {
mq.qs = append(mq.qs, NewNFQueue(i)) // qs []*NFQueue
} // }
return mq //
} // func NewMultiQueue(min, max uint16) (mq *multiQueue) {
// mq = &multiQueue{make([]*NFQueue, 0, max-min)}
func (mq *multiQueue) Process() <-chan packet.Packet { // for i := min; i < max; i++ {
var ( // mq.qs = append(mq.qs, NewNFQueue(i))
wg sync.WaitGroup // }
out = make(chan packet.Packet, len(mq.qs)) // return mq
) // }
for _, q := range mq.qs { //
wg.Add(1) // func (mq *multiQueue) Process() <-chan packet.Packet {
go func(ch <-chan packet.Packet) { // var (
for pkt := range ch { // wg sync.WaitGroup
out <- pkt // out = make(chan packet.Packet, len(mq.qs))
} // )
wg.Done() // for _, q := range mq.qs {
}(q.Process()) // wg.Add(1)
} // go func(ch <-chan packet.Packet) {
go func() { // for pkt := range ch {
wg.Wait() // out <- pkt
close(out) // }
}() // wg.Done()
return out // }(q.Process())
} // }
func (mq *multiQueue) Destroy() { // go func() {
for _, q := range mq.qs { // wg.Wait()
q.Destroy() // close(out)
} // }()
} // return out
// }
// func (mq *multiQueue) Destroy() {
// for _, q := range mq.qs {
// q.Destroy()
// }
// }

View file

@ -17,17 +17,19 @@ import (
"syscall" "syscall"
"time" "time"
"unsafe" "unsafe"
"errors"
"fmt"
"github.com/Safing/safing-core/network/packet" "github.com/Safing/portmaster/network/packet"
) )
var queues map[uint16]*nfQueue var queues map[uint16]*NFQueue
func init() { func init() {
queues = make(map[uint16]*nfQueue) queues = make(map[uint16]*NFQueue)
} }
type nfQueue struct { type NFQueue struct {
DefaultVerdict uint32 DefaultVerdict uint32
Timeout time.Duration Timeout time.Duration
qid uint16 qid uint16
@ -38,83 +40,77 @@ type nfQueue struct {
fd int fd int
lk sync.Mutex lk sync.Mutex
pktch chan packet.Packet Packets chan packet.Packet
} }
func NewNFQueue(qid uint16) (nfq *nfQueue) { func NewNFQueue(qid uint16) (nfq *NFQueue, err error) {
if os.Geteuid() != 0 { if os.Geteuid() != 0 {
panic("Must be ran by root.") return nil, errors.New("must be root to intercept packets")
} }
nfq = &nfQueue{DefaultVerdict: NFQ_ACCEPT, Timeout: 100 * time.Millisecond, qid: qid, qidptr: &qid} nfq = &NFQueue{DefaultVerdict: NFQ_ACCEPT, Timeout: 100 * time.Millisecond, qid: qid, qidptr: &qid}
queues[nfq.qid] = nfq queues[nfq.qid] = nfq
return nfq
}
/* err = nfq.init()
This returns a channel that will recieve packets, if err != nil {
the user then must call pkt.Accept() or pkt.Drop() return nil, err
*/
func (this *nfQueue) Process() <-chan packet.Packet {
if this.h != nil {
return this.pktch
} }
this.init()
go func() { go func() {
runtime.LockOSThread() runtime.LockOSThread()
C.loop_for_packets(this.h) C.loop_for_packets(nfq.h)
}() }()
return this.pktch return nfq, nil
} }
func (this *nfQueue) init() { func (this *NFQueue) init() error {
var err error var err error
if this.h, err = C.nfq_open(); err != nil || this.h == nil { if this.h, err = C.nfq_open(); err != nil || this.h == nil {
panic(err) fmt.Errorf("could not open nfqueue: %s", err)
} }
//if this.qh, err = C.nfq_create_queue(this.h, qid, C.get_cb(), unsafe.Pointer(nfq)); err != nil || this.qh == nil { //if this.qh, err = C.nfq_create_queue(this.h, qid, C.get_cb(), unsafe.Pointer(nfq)); err != nil || this.qh == nil {
this.pktch = make(chan packet.Packet, 1) this.Packets = make(chan packet.Packet, 1)
if C.nfq_unbind_pf(this.h, C.AF_INET) < 0 { if C.nfq_unbind_pf(this.h, C.AF_INET) < 0 {
this.Destroy() this.Destroy()
panic("nfq_unbind_pf(AF_INET) failed, are you running root?.") return errors.New("nfq_unbind_pf(AF_INET) failed, are you root?")
} }
if C.nfq_unbind_pf(this.h, C.AF_INET6) < 0 { if C.nfq_unbind_pf(this.h, C.AF_INET6) < 0 {
this.Destroy() this.Destroy()
panic("nfq_unbind_pf(AF_INET6) failed.") return errors.New("nfq_unbind_pf(AF_INET6) failed")
} }
if C.nfq_bind_pf(this.h, C.AF_INET) < 0 { if C.nfq_bind_pf(this.h, C.AF_INET) < 0 {
this.Destroy() this.Destroy()
panic("nfq_bind_pf(AF_INET) failed.") return errors.New("nfq_bind_pf(AF_INET) failed")
} }
if C.nfq_bind_pf(this.h, C.AF_INET6) < 0 { if C.nfq_bind_pf(this.h, C.AF_INET6) < 0 {
this.Destroy() this.Destroy()
panic("nfq_bind_pf(AF_INET6) failed.") return errors.New("nfq_bind_pf(AF_INET6) failed")
} }
if this.qh, err = C.create_queue(this.h, C.uint16_t(this.qid)); err != nil || this.qh == nil { if this.qh, err = C.create_queue(this.h, C.uint16_t(this.qid)); err != nil || this.qh == nil {
C.nfq_close(this.h) C.nfq_close(this.h)
panic(err) return fmt.Errorf("could not create queue: %s", err)
} }
this.fd = int(C.nfq_fd(this.h)) this.fd = int(C.nfq_fd(this.h))
if C.nfq_set_mode(this.qh, C.NFQNL_COPY_PACKET, 0xffff) < 0 { if C.nfq_set_mode(this.qh, C.NFQNL_COPY_PACKET, 0xffff) < 0 {
this.Destroy() this.Destroy()
panic("nfq_set_mode(NFQNL_COPY_PACKET) failed.") return errors.New("nfq_set_mode(NFQNL_COPY_PACKET) failed")
} }
if C.nfq_set_queue_maxlen(this.qh, 1024*8) < 0 { if C.nfq_set_queue_maxlen(this.qh, 1024*8) < 0 {
this.Destroy() this.Destroy()
panic("nfq_set_queue_maxlen(1024 * 8) failed.") return errors.New("nfq_set_queue_maxlen(1024 * 8) failed")
} }
return nil
} }
func (this *nfQueue) Destroy() { func (this *NFQueue) Destroy() {
this.lk.Lock() this.lk.Lock()
defer this.lk.Unlock() defer this.lk.Unlock()
@ -131,12 +127,12 @@ func (this *nfQueue) Destroy() {
} }
// TODO: don't close, we're exiting anyway // TODO: don't close, we're exiting anyway
// if this.pktch != nil { // if this.Packets != nil {
// close(this.pktch) // close(this.Packets)
// } // }
} }
func (this *nfQueue) Valid() bool { func (this *NFQueue) Valid() bool {
return this.h != nil && this.qh != nil return this.h != nil && this.qh != nil
} }
@ -148,7 +144,7 @@ func go_nfq_callback(id uint32, hwproto uint16, hook uint8, mark *uint32,
qidptr := (*uint16)(data) qidptr := (*uint16)(data)
qid := uint16(*qidptr) qid := uint16(*qidptr)
// nfq := (*nfQueue)(nfqptr) // nfq := (*NFQueue)(nfqptr)
new_version := version new_version := version
ipver := packet.IPVersion(new_version) ipver := packet.IPVersion(new_version)
ipsz := C.int(ipver.ByteSize()) ipsz := C.int(ipver.ByteSize())
@ -187,7 +183,7 @@ func go_nfq_callback(id uint32, hwproto uint16, hook uint8, mark *uint32,
// fmt.Printf("%s queuing packet\n", time.Now().Format("060102 15:04:05.000")) // fmt.Printf("%s queuing packet\n", time.Now().Format("060102 15:04:05.000"))
// BUG: "panic: send on closed channel" when shutting down // BUG: "panic: send on closed channel" when shutting down
queues[qid].pktch <- &pkt queues[qid].Packets <- &pkt
select { select {
case v = <-pkt.verdict: case v = <-pkt.verdict:

View file

@ -5,7 +5,7 @@ package nfqueue
import ( import (
"fmt" "fmt"
"github.com/Safing/safing-core/network/packet" "github.com/Safing/portmaster/network/packet"
) )
var ( var (

View file

@ -7,10 +7,10 @@ import (
"net" "net"
"time" "time"
"github.com/Safing/safing-core/database" "github.com/Safing/portbase/database"
"github.com/Safing/safing-core/intel" "github.com/Safing/portmaster/intel"
"github.com/Safing/safing-core/network/packet" "github.com/Safing/portmaster/network/packet"
"github.com/Safing/safing-core/process" "github.com/Safing/portmaster/process"
datastore "github.com/ipfs/go-datastore" datastore "github.com/ipfs/go-datastore"
) )

View file

@ -9,9 +9,9 @@ import (
datastore "github.com/ipfs/go-datastore" datastore "github.com/ipfs/go-datastore"
"github.com/Safing/safing-core/database" "github.com/Safing/portbase/database"
"github.com/Safing/safing-core/log" "github.com/Safing/portbase/log"
"github.com/Safing/safing-core/network/packet" "github.com/Safing/portmaster/network/packet"
) )
type FirewallHandler func(pkt packet.Packet, link *Link) type FirewallHandler func(pkt packet.Packet, link *Link)

View file

@ -7,8 +7,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/Safing/safing-core/crypto/random" "github.com/Safing/portbase/crypto/random"
"github.com/Safing/safing-core/intel" "github.com/Safing/portmaster/intel"
"github.com/miekg/dns" "github.com/miekg/dns"
) )