From dbfa249a95282ff5edd7632421435f780f6541c6 Mon Sep 17 00:00:00 2001 From: rabbitstack Date: Tue, 11 Mar 2025 18:47:42 +0100 Subject: [PATCH 1/3] fix(action): Declare WFP session as dynamic Dynamic sessions are meant for adding firewall configuration that should not outlast program's execution. --- pkg/rules/action/isolate_windows.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/rules/action/isolate_windows.go b/pkg/rules/action/isolate_windows.go index 1d1dd8b24..1e5f0b8bc 100644 --- a/pkg/rules/action/isolate_windows.go +++ b/pkg/rules/action/isolate_windows.go @@ -53,7 +53,9 @@ type firewall struct { } func newFirewall() (*firewall, error) { - opts := &wf.Options{} + opts := &wf.Options{ + Dynamic: true, // remove filters when the process terminates + } sess, err := wf.New(opts) if err != nil { From 7b6f483f6e989f82e0c1018d18b70cf329121269 Mon Sep 17 00:00:00 2001 From: rabbitstack Date: Wed, 12 Mar 2025 20:51:17 +0100 Subject: [PATCH 2/3] fix(action): Prevent deadlock when adding whitelist IP addresses --- pkg/rules/action/isolate_windows.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/rules/action/isolate_windows.go b/pkg/rules/action/isolate_windows.go index 1e5f0b8bc..4b54ebc65 100644 --- a/pkg/rules/action/isolate_windows.go +++ b/pkg/rules/action/isolate_windows.go @@ -157,8 +157,6 @@ func (f *firewall) hasAllowRules() bool { } func (f *firewall) addIPCondition(addr net.IP) { - f.mu.Lock() - defer f.mu.Unlock() ip := netip.AddrFrom4([4]byte(addr)) f.inbound.Conditions = append(f.inbound.Conditions, &wf.Match{Field: wf.FieldIPLocalAddress, Op: wf.MatchTypeEqual, Value: ip}) f.inbound.Conditions = append(f.inbound.Conditions, &wf.Match{Field: wf.FieldIPRemoteAddress, Op: wf.MatchTypeEqual, Value: ip}) From d41bff33efda35b119431d3f38f9ec6f78a78296 Mon Sep 17 00:00:00 2001 From: rabbitstack Date: Thu, 13 Mar 2025 21:01:16 +0100 Subject: [PATCH 3/3] fix(action): Add single IP condition The current limitation of the fw library, makes it impossible to install a filter with multiple IP addresses, so we shrink the list to always append the first IP address in the list. --- pkg/rules/action/isolate_windows.go | 87 +++++++++++++---------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/pkg/rules/action/isolate_windows.go b/pkg/rules/action/isolate_windows.go index 4b54ebc65..4c4cd1a5e 100644 --- a/pkg/rules/action/isolate_windows.go +++ b/pkg/rules/action/isolate_windows.go @@ -69,25 +69,28 @@ func (f *firewall) allow(whitelist []net.IP) error { f.mu.Lock() defer f.mu.Unlock() f.inbound = &wf.Rule{ - ID: inboundAllowRuleID, - Name: inboundAllowRuleName, - Layer: wf.LayerInboundIPPacketV4, - Action: wf.ActionPermit, - Conditions: []*wf.Match{ - {Field: wf.FieldIPLocalAddress, Op: wf.MatchTypeEqual, Value: netip.AddrFrom4([4]byte{127, 0, 0, 1})}, - {Field: wf.FieldIPRemoteAddress, Op: wf.MatchTypeEqual, Value: netip.AddrFrom4([4]byte{127, 0, 0, 1})}, - }, + ID: inboundAllowRuleID, + Name: inboundAllowRuleName, + Layer: wf.LayerInboundIPPacketV4, + Action: wf.ActionPermit, + Conditions: make([]*wf.Match, 0), } f.outbound = &wf.Rule{ - ID: outboundAllowRuleID, - Name: outboundAllowRuleName, - Layer: wf.LayerOutboundIPPacketV4, - Action: wf.ActionPermit, - Conditions: []*wf.Match{ - {Field: wf.FieldIPLocalAddress, Op: wf.MatchTypeEqual, Value: netip.AddrFrom4([4]byte{127, 0, 0, 1})}, - {Field: wf.FieldIPRemoteAddress, Op: wf.MatchTypeEqual, Value: netip.AddrFrom4([4]byte{127, 0, 0, 1})}, - }, + ID: outboundAllowRuleID, + Name: outboundAllowRuleName, + Layer: wf.LayerOutboundIPPacketV4, + Action: wf.ActionPermit, + Conditions: make([]*wf.Match, 0), + } + + // The current limitation of the fw + // library, makes it impossible to + // install a filter with multiple IP + // addresses, so we shrink the list + // to use a single item + if len(whitelist) > 0 { + whitelist = whitelist[:1] } for _, addr := range whitelist { @@ -144,6 +147,7 @@ func (f *firewall) findAllowRules() error { return nil } +//nolint:unused func (f *firewall) removeAllowRules() error { f.mu.Lock() defer f.mu.Unlock() @@ -157,13 +161,16 @@ func (f *firewall) hasAllowRules() bool { } func (f *firewall) addIPCondition(addr net.IP) { - ip := netip.AddrFrom4([4]byte(addr)) - f.inbound.Conditions = append(f.inbound.Conditions, &wf.Match{Field: wf.FieldIPLocalAddress, Op: wf.MatchTypeEqual, Value: ip}) - f.inbound.Conditions = append(f.inbound.Conditions, &wf.Match{Field: wf.FieldIPRemoteAddress, Op: wf.MatchTypeEqual, Value: ip}) - f.outbound.Conditions = append(f.outbound.Conditions, &wf.Match{Field: wf.FieldIPLocalAddress, Op: wf.MatchTypeEqual, Value: ip}) - f.outbound.Conditions = append(f.outbound.Conditions, &wf.Match{Field: wf.FieldIPRemoteAddress, Op: wf.MatchTypeEqual, Value: ip}) + ip, err := netip.ParseAddr(addr.String()) + if err == nil { + f.inbound.Conditions = append(f.inbound.Conditions, &wf.Match{Field: wf.FieldIPLocalAddress, Op: wf.MatchTypeEqual, Value: ip}) + f.inbound.Conditions = append(f.inbound.Conditions, &wf.Match{Field: wf.FieldIPRemoteAddress, Op: wf.MatchTypeEqual, Value: ip}) + f.outbound.Conditions = append(f.outbound.Conditions, &wf.Match{Field: wf.FieldIPLocalAddress, Op: wf.MatchTypeEqual, Value: ip}) + f.outbound.Conditions = append(f.outbound.Conditions, &wf.Match{Field: wf.FieldIPRemoteAddress, Op: wf.MatchTypeEqual, Value: ip}) + } } +//nolint:unused func (f *firewall) hasIPCondition(addr net.IP) bool { f.mu.Lock() defer f.mu.Unlock() @@ -177,7 +184,12 @@ func (f *firewall) hasIPCondition(addr net.IP) bool { continue } - if netip.AddrFrom4([4]byte(addr)) == address { + netaddr, err := netip.ParseAddr(addr.String()) + if err != nil { + continue + } + + if netaddr == address { return true } } @@ -192,7 +204,12 @@ func (f *firewall) hasIPCondition(addr net.IP) bool { continue } - if netip.AddrFrom4([4]byte(addr)) == address { + netaddr, err := netip.ParseAddr(addr.String()) + if err != nil { + continue + } + + if netaddr == address { return true } } @@ -223,30 +240,6 @@ func Isolate(whitelist []net.IP) error { } switch { - case fw.hasAllowRules() && len(whitelist) > 0: - // rules were added and the whitelist - // is given in the action. Check if - // the given permitted addresses contain - // an item that is not already in the - // allowed rules conditions. - refresh := true - for _, addr := range whitelist { - if fw.hasIPCondition(addr) { - refresh = false - break - } else { - fw.addIPCondition(addr) - } - } - - if refresh { - if err := fw.removeAllowRules(); err != nil { - return err - } - return fw.allow(whitelist) - } - - return nil case fw.hasAllowRules(): // rules were added and no new permitted // addresses are supplied in the action