| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | package nebula | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-06-30 18:53:30 -04:00
										 |  |  | 	"crypto/sha256" | 
					
						
							|  |  |  | 	"encoding/hex" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2023-11-28 11:56:47 -05:00
										 |  |  | 	"hash/fnv" | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 	"net/netip" | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2020-06-30 18:53:30 -04:00
										 |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 	"github.com/gaissmai/bart" | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	"github.com/rcrowley/go-metrics" | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	"github.com/sirupsen/logrus" | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	"github.com/slackhq/nebula/cert" | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	"github.com/slackhq/nebula/config" | 
					
						
							|  |  |  | 	"github.com/slackhq/nebula/firewall" | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type FirewallInterface interface { | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	AddRule(incoming bool, proto uint8, startPort int32, endPort int32, groups []string, host string, addr, localAddr netip.Prefix, caName string, caSha string) error | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type conn struct { | 
					
						
							|  |  |  | 	Expires time.Time // Time when this conntrack entry will expire | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// record why the original connection passed the firewall, so we can re-validate | 
					
						
							|  |  |  | 	// after ruleset changes. Note, rulesVersion is a uint16 so that these two | 
					
						
							|  |  |  | 	// fields pack for free after the uint32 above | 
					
						
							|  |  |  | 	incoming     bool | 
					
						
							|  |  |  | 	rulesVersion uint16 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TODO: need conntrack max tracked connections handling | 
					
						
							|  |  |  | type Firewall struct { | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	Conntrack *FirewallConntrack | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	InRules  *FirewallTable | 
					
						
							|  |  |  | 	OutRules *FirewallTable | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 15:08:40 -04:00
										 |  |  | 	InSendReject  bool | 
					
						
							|  |  |  | 	OutSendReject bool | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	//TODO: we should have many more options for TCP, an option for ICMP, and mimic the kernel a bit better | 
					
						
							|  |  |  | 	// https://www.kernel.org/doc/Documentation/networking/nf_conntrack-sysctl.txt | 
					
						
							|  |  |  | 	TCPTimeout     time.Duration //linux: 5 days max | 
					
						
							|  |  |  | 	UDPTimeout     time.Duration //linux: 180s max | 
					
						
							|  |  |  | 	DefaultTimeout time.Duration //linux: 600s | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	// routableNetworks describes the vpn addresses as well as any unsafe networks issued to us in the certificate. | 
					
						
							|  |  |  | 	// The vpn addresses are a full bit match while the unsafe networks only match the prefix | 
					
						
							|  |  |  | 	routableNetworks *bart.Table[struct{}] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// assignedNetworks is a list of vpn networks assigned to us in the certificate. | 
					
						
							|  |  |  | 	assignedNetworks  []netip.Prefix | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 	hasUnsafeNetworks bool | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	rules        string | 
					
						
							|  |  |  | 	rulesVersion uint16 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-15 11:44:05 -06:00
										 |  |  | 	defaultLocalCIDRAny bool | 
					
						
							|  |  |  | 	incomingMetrics     firewallMetrics | 
					
						
							|  |  |  | 	outgoingMetrics     firewallMetrics | 
					
						
							| 
									
										
										
										
											2021-04-27 22:23:18 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	l *logrus.Logger | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type firewallMetrics struct { | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	droppedLocalAddr  metrics.Counter | 
					
						
							|  |  |  | 	droppedRemoteAddr metrics.Counter | 
					
						
							|  |  |  | 	droppedNoRule     metrics.Counter | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | type FirewallConntrack struct { | 
					
						
							|  |  |  | 	sync.Mutex | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	Conns      map[firewall.Packet]*conn | 
					
						
							| 
									
										
										
										
											2023-01-18 10:56:42 -06:00
										 |  |  | 	TimerWheel *TimerWheel[firewall.Packet] | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 16:02:10 -06:00
										 |  |  | // FirewallTable is the entry point for a rule, the evaluation order is: | 
					
						
							|  |  |  | // Proto AND port AND (CA SHA or CA name) AND local CIDR AND (group OR groups OR name OR remote CIDR) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | type FirewallTable struct { | 
					
						
							|  |  |  | 	TCP      firewallPort | 
					
						
							|  |  |  | 	UDP      firewallPort | 
					
						
							|  |  |  | 	ICMP     firewallPort | 
					
						
							|  |  |  | 	AnyProto firewallPort | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newFirewallTable() *FirewallTable { | 
					
						
							|  |  |  | 	return &FirewallTable{ | 
					
						
							|  |  |  | 		TCP:      firewallPort{}, | 
					
						
							|  |  |  | 		UDP:      firewallPort{}, | 
					
						
							|  |  |  | 		ICMP:     firewallPort{}, | 
					
						
							|  |  |  | 		AnyProto: firewallPort{}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | type FirewallCA struct { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	Any     *FirewallRule | 
					
						
							|  |  |  | 	CANames map[string]*FirewallRule | 
					
						
							|  |  |  | 	CAShas  map[string]*FirewallRule | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | type FirewallRule struct { | 
					
						
							| 
									
										
										
										
											2024-01-23 16:02:10 -06:00
										 |  |  | 	// Any makes Hosts, Groups, and CIDR irrelevant | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	Any    *firewallLocalCIDR | 
					
						
							|  |  |  | 	Hosts  map[string]*firewallLocalCIDR | 
					
						
							|  |  |  | 	Groups []*firewallGroups | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 	CIDR   *bart.Table[*firewallLocalCIDR] | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type firewallGroups struct { | 
					
						
							|  |  |  | 	Groups    []string | 
					
						
							|  |  |  | 	LocalCIDR *firewallLocalCIDR | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Even though ports are uint16, int32 maps are faster for lookup | 
					
						
							|  |  |  | // Plus we can use `-1` for fragment rules | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | type firewallPort map[int32]*FirewallCA | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 16:02:10 -06:00
										 |  |  | type firewallLocalCIDR struct { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	Any       bool | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 	LocalCIDR *bart.Table[struct{}] | 
					
						
							| 
									
										
										
										
											2024-01-23 16:02:10 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | // NewFirewall creates a new Firewall object. A TimerWheel is created for you from the provided timeouts. | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | // The certificate provided should be the highest version loaded in memory. | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | func NewFirewall(l *logrus.Logger, tcpTimeout, UDPTimeout, defaultTimeout time.Duration, c cert.Certificate) *Firewall { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	//TODO: error on 0 duration | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	var tmin, tmax time.Duration | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if tcpTimeout < UDPTimeout { | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 		tmin = tcpTimeout | 
					
						
							|  |  |  | 		tmax = UDPTimeout | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 		tmin = UDPTimeout | 
					
						
							|  |  |  | 		tmax = tcpTimeout | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	if defaultTimeout < tmin { | 
					
						
							|  |  |  | 		tmin = defaultTimeout | 
					
						
							|  |  |  | 	} else if defaultTimeout > tmax { | 
					
						
							|  |  |  | 		tmax = defaultTimeout | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	routableNetworks := new(bart.Table[struct{}]) | 
					
						
							|  |  |  | 	var assignedNetworks []netip.Prefix | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 	for _, network := range c.Networks() { | 
					
						
							|  |  |  | 		nprefix := netip.PrefixFrom(network.Addr(), network.Addr().BitLen()) | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 		routableNetworks.Insert(nprefix, struct{}{}) | 
					
						
							|  |  |  | 		assignedNetworks = append(assignedNetworks, network) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 	hasUnsafeNetworks := false | 
					
						
							|  |  |  | 	for _, n := range c.UnsafeNetworks() { | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 		routableNetworks.Insert(n, struct{}{}) | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 		hasUnsafeNetworks = true | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &Firewall{ | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 		Conntrack: &FirewallConntrack{ | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 			Conns:      make(map[firewall.Packet]*conn), | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 			TimerWheel: NewTimerWheel[firewall.Packet](tmin, tmax), | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 		InRules:           newFirewallTable(), | 
					
						
							|  |  |  | 		OutRules:          newFirewallTable(), | 
					
						
							|  |  |  | 		TCPTimeout:        tcpTimeout, | 
					
						
							|  |  |  | 		UDPTimeout:        UDPTimeout, | 
					
						
							|  |  |  | 		DefaultTimeout:    defaultTimeout, | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 		routableNetworks:  routableNetworks, | 
					
						
							|  |  |  | 		assignedNetworks:  assignedNetworks, | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 		hasUnsafeNetworks: hasUnsafeNetworks, | 
					
						
							|  |  |  | 		l:                 l, | 
					
						
							| 
									
										
										
										
											2021-04-27 22:23:18 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		incomingMetrics: firewallMetrics{ | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 			droppedLocalAddr:  metrics.GetOrRegisterCounter("firewall.incoming.dropped.local_addr", nil), | 
					
						
							|  |  |  | 			droppedRemoteAddr: metrics.GetOrRegisterCounter("firewall.incoming.dropped.remote_addr", nil), | 
					
						
							|  |  |  | 			droppedNoRule:     metrics.GetOrRegisterCounter("firewall.incoming.dropped.no_rule", nil), | 
					
						
							| 
									
										
										
										
											2021-04-27 22:23:18 -04:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		outgoingMetrics: firewallMetrics{ | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 			droppedLocalAddr:  metrics.GetOrRegisterCounter("firewall.outgoing.dropped.local_addr", nil), | 
					
						
							|  |  |  | 			droppedRemoteAddr: metrics.GetOrRegisterCounter("firewall.outgoing.dropped.remote_addr", nil), | 
					
						
							|  |  |  | 			droppedNoRule:     metrics.GetOrRegisterCounter("firewall.outgoing.dropped.no_rule", nil), | 
					
						
							| 
									
										
										
										
											2021-04-27 22:23:18 -04:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | func NewFirewallFromConfig(l *logrus.Logger, cs *CertState, c *config.C) (*Firewall, error) { | 
					
						
							|  |  |  | 	certificate := cs.getCertificate(cert.Version2) | 
					
						
							|  |  |  | 	if certificate == nil { | 
					
						
							|  |  |  | 		certificate = cs.getCertificate(cert.Version1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if certificate == nil { | 
					
						
							|  |  |  | 		panic("No certificate available to reconfigure the firewall") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	fw := NewFirewall( | 
					
						
							| 
									
										
										
										
											2021-03-26 09:46:30 -05:00
										 |  |  | 		l, | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 		c.GetDuration("firewall.conntrack.tcp_timeout", time.Minute*12), | 
					
						
							|  |  |  | 		c.GetDuration("firewall.conntrack.udp_timeout", time.Minute*3), | 
					
						
							|  |  |  | 		c.GetDuration("firewall.conntrack.default_timeout", time.Minute*10), | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 		certificate, | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		//TODO: max_connections | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	fw.defaultLocalCIDRAny = c.GetBool("firewall.default_local_cidr_any", false) | 
					
						
							| 
									
										
										
										
											2024-02-15 11:44:05 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 15:08:40 -04:00
										 |  |  | 	inboundAction := c.GetString("firewall.inbound_action", "drop") | 
					
						
							|  |  |  | 	switch inboundAction { | 
					
						
							|  |  |  | 	case "reject": | 
					
						
							|  |  |  | 		fw.InSendReject = true | 
					
						
							|  |  |  | 	case "drop": | 
					
						
							|  |  |  | 		fw.InSendReject = false | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		l.WithField("action", inboundAction).Warn("invalid firewall.inbound_action, defaulting to `drop`") | 
					
						
							|  |  |  | 		fw.InSendReject = false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outboundAction := c.GetString("firewall.outbound_action", "drop") | 
					
						
							|  |  |  | 	switch outboundAction { | 
					
						
							|  |  |  | 	case "reject": | 
					
						
							|  |  |  | 		fw.OutSendReject = true | 
					
						
							|  |  |  | 	case "drop": | 
					
						
							|  |  |  | 		fw.OutSendReject = false | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		l.WithField("action", inboundAction).Warn("invalid firewall.outbound_action, defaulting to `drop`") | 
					
						
							|  |  |  | 		fw.OutSendReject = false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-26 09:46:30 -05:00
										 |  |  | 	err := AddFirewallRulesFromConfig(l, false, c, fw) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-26 09:46:30 -05:00
										 |  |  | 	err = AddFirewallRulesFromConfig(l, true, c, fw) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fw, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AddRule properly creates the in memory rule structure for a firewall table. | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | func (f *Firewall) AddRule(incoming bool, proto uint8, startPort int32, endPort int32, groups []string, host string, ip, localIp netip.Prefix, caName string, caSha string) error { | 
					
						
							| 
									
										
										
										
											2020-09-18 09:20:09 -05:00
										 |  |  | 	// Under gomobile, stringing a nil pointer with fmt causes an abort in debug mode for iOS | 
					
						
							|  |  |  | 	// https://github.com/golang/go/issues/14131 | 
					
						
							|  |  |  | 	sIp := "" | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 	if ip.IsValid() { | 
					
						
							| 
									
										
										
										
											2020-09-18 09:20:09 -05:00
										 |  |  | 		sIp = ip.String() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-05-09 16:37:23 +01:00
										 |  |  | 	lIp := "" | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 	if localIp.IsValid() { | 
					
						
							| 
									
										
										
										
											2023-05-09 16:37:23 +01:00
										 |  |  | 		lIp = localIp.String() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// We need this rule string because we generate a hash. Removing this will break firewall reload. | 
					
						
							|  |  |  | 	ruleString := fmt.Sprintf( | 
					
						
							| 
									
										
										
										
											2023-05-09 16:37:23 +01:00
										 |  |  | 		"incoming: %v, proto: %v, startPort: %v, endPort: %v, groups: %v, host: %v, ip: %v, localIp: %v, caName: %v, caSha: %s", | 
					
						
							|  |  |  | 		incoming, proto, startPort, endPort, groups, host, sIp, lIp, caName, caSha, | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	f.rules += ruleString + "\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	direction := "incoming" | 
					
						
							|  |  |  | 	if !incoming { | 
					
						
							|  |  |  | 		direction = "outgoing" | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-05-09 16:37:23 +01:00
										 |  |  | 	f.l.WithField("firewallRule", m{"direction": direction, "proto": proto, "startPort": startPort, "endPort": endPort, "groups": groups, "host": host, "ip": sIp, "localIp": lIp, "caName": caName, "caSha": caSha}). | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		Info("Firewall rule added") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		ft *FirewallTable | 
					
						
							|  |  |  | 		fp firewallPort | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if incoming { | 
					
						
							|  |  |  | 		ft = f.InRules | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ft = f.OutRules | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch proto { | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	case firewall.ProtoTCP: | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		fp = ft.TCP | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	case firewall.ProtoUDP: | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		fp = ft.UDP | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	case firewall.ProtoICMP, firewall.ProtoICMPv6: | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		fp = ft.ICMP | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	case firewall.ProtoAny: | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		fp = ft.AnyProto | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return fmt.Errorf("unknown protocol %v", proto) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	return fp.addRule(f, startPort, endPort, groups, host, ip, localIp, caName, caSha) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetRuleHash returns a hash representation of all inbound and outbound rules | 
					
						
							|  |  |  | func (f *Firewall) GetRuleHash() string { | 
					
						
							|  |  |  | 	sum := sha256.Sum256([]byte(f.rules)) | 
					
						
							|  |  |  | 	return hex.EncodeToString(sum[:]) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-28 11:56:47 -05:00
										 |  |  | // GetRuleHashFNV returns a uint32 FNV-1 hash representation the rules, for use as a metric value | 
					
						
							|  |  |  | func (f *Firewall) GetRuleHashFNV() uint32 { | 
					
						
							|  |  |  | 	h := fnv.New32a() | 
					
						
							|  |  |  | 	h.Write([]byte(f.rules)) | 
					
						
							|  |  |  | 	return h.Sum32() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetRuleHashes returns both the sha256 and FNV-1 hashes, suitable for logging | 
					
						
							|  |  |  | func (f *Firewall) GetRuleHashes() string { | 
					
						
							|  |  |  | 	return "SHA:" + f.GetRuleHash() + ",FNV:" + strconv.FormatUint(uint64(f.GetRuleHashFNV()), 10) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | func AddFirewallRulesFromConfig(l *logrus.Logger, inbound bool, c *config.C, fw FirewallInterface) error { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	var table string | 
					
						
							|  |  |  | 	if inbound { | 
					
						
							|  |  |  | 		table = "firewall.inbound" | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		table = "firewall.outbound" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	r := c.Get(table) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	if r == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rs, ok := r.([]interface{}) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return fmt.Errorf("%s failed to parse, should be an array of rules", table) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, t := range rs { | 
					
						
							|  |  |  | 		var groups []string | 
					
						
							| 
									
										
										
										
											2021-03-26 09:46:30 -05:00
										 |  |  | 		r, err := convertRule(l, t, table, i) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("%s rule #%v; %s", table, i, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if r.Code != "" && r.Port != "" { | 
					
						
							|  |  |  | 			return fmt.Errorf("%s rule #%v; only one of port or code should be provided", table, i) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-09 16:37:23 +01:00
										 |  |  | 		if r.Host == "" && len(r.Groups) == 0 && r.Group == "" && r.Cidr == "" && r.LocalCidr == "" && r.CAName == "" && r.CASha == "" { | 
					
						
							|  |  |  | 			return fmt.Errorf("%s rule #%v; at least one of host, group, cidr, local_cidr, ca_name, or ca_sha must be provided", table, i) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if len(r.Groups) > 0 { | 
					
						
							|  |  |  | 			groups = r.Groups | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if r.Group != "" { | 
					
						
							|  |  |  | 			// Check if we have both groups and group provided in the rule config | 
					
						
							|  |  |  | 			if len(groups) > 0 { | 
					
						
							|  |  |  | 				return fmt.Errorf("%s rule #%v; only one of group or groups should be defined, both provided", table, i) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			groups = []string{r.Group} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var sPort, errPort string | 
					
						
							|  |  |  | 		if r.Code != "" { | 
					
						
							|  |  |  | 			errPort = "code" | 
					
						
							|  |  |  | 			sPort = r.Code | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			errPort = "port" | 
					
						
							|  |  |  | 			sPort = r.Port | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		startPort, endPort, err := parsePort(sPort) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("%s rule #%v; %s %s", table, i, errPort, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var proto uint8 | 
					
						
							|  |  |  | 		switch r.Proto { | 
					
						
							|  |  |  | 		case "any": | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 			proto = firewall.ProtoAny | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		case "tcp": | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 			proto = firewall.ProtoTCP | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		case "udp": | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 			proto = firewall.ProtoUDP | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		case "icmp": | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 			proto = firewall.ProtoICMP | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return fmt.Errorf("%s rule #%v; proto was not understood; `%s`", table, i, r.Proto) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 		var cidr netip.Prefix | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		if r.Cidr != "" { | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 			cidr, err = netip.ParsePrefix(r.Cidr) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return fmt.Errorf("%s rule #%v; cidr did not parse; %s", table, i, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 		var localCidr netip.Prefix | 
					
						
							| 
									
										
										
										
											2023-05-09 16:37:23 +01:00
										 |  |  | 		if r.LocalCidr != "" { | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 			localCidr, err = netip.ParsePrefix(r.LocalCidr) | 
					
						
							| 
									
										
										
										
											2023-05-09 16:37:23 +01:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return fmt.Errorf("%s rule #%v; local_cidr did not parse; %s", table, i, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		err = fw.AddRule(inbound, proto, startPort, endPort, groups, r.Host, cidr, localCidr, r.CAName, r.CASha) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("%s rule #%v; `%s`", table, i, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-10 10:57:21 -07:00
										 |  |  | var ErrInvalidRemoteIP = errors.New("remote IP is not in remote certificate subnets") | 
					
						
							|  |  |  | var ErrInvalidLocalIP = errors.New("local IP is not in list of handled local IPs") | 
					
						
							|  |  |  | var ErrNoMatchingRule = errors.New("no matching rule in firewall table") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Drop returns an error if the packet should be dropped, explaining why. It | 
					
						
							|  |  |  | // returns nil if the packet should not be dropped. | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | func (f *Firewall) Drop(fp firewall.Packet, incoming bool, h *HostInfo, caPool *cert.CAPool, localCache firewall.ConntrackCache) error { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	// Check if we spoke to this tuple, if we did then allow this packet | 
					
						
							| 
									
										
										
										
											2024-04-11 21:44:22 -05:00
										 |  |  | 	if f.inConns(fp, h, caPool, localCache) { | 
					
						
							| 
									
										
										
										
											2020-04-10 10:57:21 -07:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 16:34:17 +00:00
										 |  |  | 	// Make sure remote address matches nebula certificate | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	if h.networks != nil { | 
					
						
							|  |  |  | 		_, ok := h.networks.Lookup(fp.RemoteAddr) | 
					
						
							| 
									
										
										
										
											2023-11-02 17:05:08 -05:00
										 |  |  | 		if !ok { | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 			f.metrics(incoming).droppedRemoteAddr.Inc(1) | 
					
						
							| 
									
										
										
										
											2020-04-10 10:57:21 -07:00
										 |  |  | 			return ErrInvalidRemoteIP | 
					
						
							| 
									
										
										
										
											2020-03-02 16:21:33 -05:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 		// Simple case: Certificate has one address and no unsafe networks | 
					
						
							|  |  |  | 		if h.vpnAddrs[0] != fp.RemoteAddr { | 
					
						
							|  |  |  | 			f.metrics(incoming).droppedRemoteAddr.Inc(1) | 
					
						
							| 
									
										
										
										
											2020-04-10 10:57:21 -07:00
										 |  |  | 			return ErrInvalidRemoteIP | 
					
						
							| 
									
										
										
										
											2020-03-02 16:21:33 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-12-12 16:34:17 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	// Make sure we are supposed to be handling this local ip address | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	_, ok := f.routableNetworks.Lookup(fp.LocalAddr) | 
					
						
							| 
									
										
										
										
											2023-11-02 17:05:08 -05:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 		f.metrics(incoming).droppedLocalAddr.Inc(1) | 
					
						
							| 
									
										
										
										
											2020-04-10 10:57:21 -07:00
										 |  |  | 		return ErrInvalidLocalIP | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	table := f.OutRules | 
					
						
							|  |  |  | 	if incoming { | 
					
						
							|  |  |  | 		table = f.InRules | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// We now know which firewall table to check against | 
					
						
							| 
									
										
										
										
											2019-12-12 16:34:17 +00:00
										 |  |  | 	if !table.match(fp, incoming, h.ConnectionState.peerCert, caPool) { | 
					
						
							| 
									
										
										
										
											2021-04-27 22:23:18 -04:00
										 |  |  | 		f.metrics(incoming).droppedNoRule.Inc(1) | 
					
						
							| 
									
										
										
										
											2020-04-10 10:57:21 -07:00
										 |  |  | 		return ErrNoMatchingRule | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// We always want to conntrack since it is a faster operation | 
					
						
							| 
									
										
										
										
											2024-04-11 21:44:22 -05:00
										 |  |  | 	f.addConn(fp, incoming) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-10 10:57:21 -07:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-27 22:23:18 -04:00
										 |  |  | func (f *Firewall) metrics(incoming bool) firewallMetrics { | 
					
						
							|  |  |  | 	if incoming { | 
					
						
							|  |  |  | 		return f.incomingMetrics | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		return f.outgoingMetrics | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | // Destroy cleans up any known cyclical references so the object can be free'd my GC. This should be called if a new | 
					
						
							|  |  |  | // firewall object is created | 
					
						
							|  |  |  | func (f *Firewall) Destroy() { | 
					
						
							|  |  |  | 	//TODO: clean references if/when needed | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (f *Firewall) EmitStats() { | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	conntrack := f.Conntrack | 
					
						
							|  |  |  | 	conntrack.Lock() | 
					
						
							|  |  |  | 	conntrackCount := len(conntrack.Conns) | 
					
						
							|  |  |  | 	conntrack.Unlock() | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	metrics.GetOrRegisterGauge("firewall.conntrack.count", nil).Update(int64(conntrackCount)) | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	metrics.GetOrRegisterGauge("firewall.rules.version", nil).Update(int64(f.rulesVersion)) | 
					
						
							| 
									
										
										
										
											2023-11-28 11:56:47 -05:00
										 |  |  | 	metrics.GetOrRegisterGauge("firewall.rules.hash", nil).Update(int64(f.GetRuleHashFNV())) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | func (f *Firewall) inConns(fp firewall.Packet, h *HostInfo, caPool *cert.CAPool, localCache firewall.ConntrackCache) bool { | 
					
						
							| 
									
										
										
										
											2021-03-01 19:52:17 -05:00
										 |  |  | 	if localCache != nil { | 
					
						
							|  |  |  | 		if _, ok := localCache[fp]; ok { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	conntrack := f.Conntrack | 
					
						
							|  |  |  | 	conntrack.Lock() | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Purge every time we test | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	ep, has := conntrack.TimerWheel.Purge() | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	if has { | 
					
						
							|  |  |  | 		f.evict(ep) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	c, ok := conntrack.Conns[fp] | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 		conntrack.Unlock() | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	if c.rulesVersion != f.rulesVersion { | 
					
						
							|  |  |  | 		// This conntrack entry was for an older rule set, validate | 
					
						
							|  |  |  | 		// it still passes with the current rule set | 
					
						
							|  |  |  | 		table := f.OutRules | 
					
						
							|  |  |  | 		if c.incoming { | 
					
						
							|  |  |  | 			table = f.InRules | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// We now know which firewall table to check against | 
					
						
							|  |  |  | 		if !table.match(fp, c.incoming, h.ConnectionState.peerCert, caPool) { | 
					
						
							| 
									
										
										
										
											2021-03-26 09:46:30 -05:00
										 |  |  | 			if f.l.Level >= logrus.DebugLevel { | 
					
						
							|  |  |  | 				h.logger(f.l). | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 					WithField("fwPacket", fp). | 
					
						
							|  |  |  | 					WithField("incoming", c.incoming). | 
					
						
							|  |  |  | 					WithField("rulesVersion", f.rulesVersion). | 
					
						
							|  |  |  | 					WithField("oldRulesVersion", c.rulesVersion). | 
					
						
							|  |  |  | 					Debugln("dropping old conntrack entry, does not match new ruleset") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			delete(conntrack.Conns, fp) | 
					
						
							|  |  |  | 			conntrack.Unlock() | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-26 09:46:30 -05:00
										 |  |  | 		if f.l.Level >= logrus.DebugLevel { | 
					
						
							|  |  |  | 			h.logger(f.l). | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 				WithField("fwPacket", fp). | 
					
						
							|  |  |  | 				WithField("incoming", c.incoming). | 
					
						
							|  |  |  | 				WithField("rulesVersion", f.rulesVersion). | 
					
						
							|  |  |  | 				WithField("oldRulesVersion", c.rulesVersion). | 
					
						
							|  |  |  | 				Debugln("keeping old conntrack entry, does match new ruleset") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		c.rulesVersion = f.rulesVersion | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	switch fp.Protocol { | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	case firewall.ProtoTCP: | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		c.Expires = time.Now().Add(f.TCPTimeout) | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	case firewall.ProtoUDP: | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		c.Expires = time.Now().Add(f.UDPTimeout) | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		c.Expires = time.Now().Add(f.DefaultTimeout) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	conntrack.Unlock() | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-01 19:52:17 -05:00
										 |  |  | 	if localCache != nil { | 
					
						
							|  |  |  | 		localCache[fp] = struct{}{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-11 21:44:22 -05:00
										 |  |  | func (f *Firewall) addConn(fp firewall.Packet, incoming bool) { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	var timeout time.Duration | 
					
						
							|  |  |  | 	c := &conn{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch fp.Protocol { | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	case firewall.ProtoTCP: | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		timeout = f.TCPTimeout | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	case firewall.ProtoUDP: | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		timeout = f.UDPTimeout | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		timeout = f.DefaultTimeout | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	conntrack := f.Conntrack | 
					
						
							|  |  |  | 	conntrack.Lock() | 
					
						
							|  |  |  | 	if _, ok := conntrack.Conns[fp]; !ok { | 
					
						
							| 
									
										
										
										
											2023-01-18 10:56:42 -06:00
										 |  |  | 		conntrack.TimerWheel.Advance(time.Now()) | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 		conntrack.TimerWheel.Add(fp, timeout) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	// Record which rulesVersion allowed this connection, so we can retest after | 
					
						
							|  |  |  | 	// firewall reload | 
					
						
							|  |  |  | 	c.incoming = incoming | 
					
						
							|  |  |  | 	c.rulesVersion = f.rulesVersion | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	c.Expires = time.Now().Add(timeout) | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	conntrack.Conns[fp] = c | 
					
						
							|  |  |  | 	conntrack.Unlock() | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Evict checks if a conntrack entry has expired, if so it is removed, if not it is re-added to the wheel | 
					
						
							|  |  |  | // Caller must own the connMutex lock! | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | func (f *Firewall) evict(p firewall.Packet) { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	// Are we still tracking this conn? | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	conntrack := f.Conntrack | 
					
						
							|  |  |  | 	t, ok := conntrack.Conns[p] | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	newT := t.Expires.Sub(time.Now()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Timeout is in the future, re-add the timer | 
					
						
							|  |  |  | 	if newT > 0 { | 
					
						
							| 
									
										
										
										
											2023-01-18 10:56:42 -06:00
										 |  |  | 		conntrack.TimerWheel.Advance(time.Now()) | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 		conntrack.TimerWheel.Add(p, newT) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// This conn is done | 
					
						
							| 
									
										
										
										
											2020-07-31 18:53:36 -04:00
										 |  |  | 	delete(conntrack.Conns, p) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | func (ft *FirewallTable) match(p firewall.Packet, incoming bool, c *cert.CachedCertificate, caPool *cert.CAPool) bool { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	if ft.AnyProto.match(p, incoming, c, caPool) { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch p.Protocol { | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	case firewall.ProtoTCP: | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		if ft.TCP.match(p, incoming, c, caPool) { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	case firewall.ProtoUDP: | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		if ft.UDP.match(p, incoming, c, caPool) { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	case firewall.ProtoICMP, firewall.ProtoICMPv6: | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		if ft.ICMP.match(p, incoming, c, caPool) { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | func (fp firewallPort) addRule(f *Firewall, startPort int32, endPort int32, groups []string, host string, ip, localIp netip.Prefix, caName string, caSha string) error { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	if startPort > endPort { | 
					
						
							|  |  |  | 		return fmt.Errorf("start port was lower than end port") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := startPort; i <= endPort; i++ { | 
					
						
							|  |  |  | 		if _, ok := fp[i]; !ok { | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 			fp[i] = &FirewallCA{ | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 				CANames: make(map[string]*FirewallRule), | 
					
						
							|  |  |  | 				CAShas:  make(map[string]*FirewallRule), | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		if err := fp[i].addRule(f, groups, host, ip, localIp, caName, caSha); err != nil { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | func (fp firewallPort) match(p firewall.Packet, incoming bool, c *cert.CachedCertificate, caPool *cert.CAPool) bool { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	// We don't have any allowed ports, bail | 
					
						
							|  |  |  | 	if fp == nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var port int32 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if p.Fragment { | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 		port = firewall.PortFragment | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} else if incoming { | 
					
						
							|  |  |  | 		port = int32(p.LocalPort) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		port = int32(p.RemotePort) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if fp[port].match(p, c, caPool) { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 	return fp[firewall.PortAny].match(p, c, caPool) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | func (fc *FirewallCA) addRule(f *Firewall, groups []string, host string, ip, localIp netip.Prefix, caName, caSha string) error { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	fr := func() *FirewallRule { | 
					
						
							|  |  |  | 		return &FirewallRule{ | 
					
						
							|  |  |  | 			Hosts:  make(map[string]*firewallLocalCIDR), | 
					
						
							|  |  |  | 			Groups: make([]*firewallGroups, 0), | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 			CIDR:   new(bart.Table[*firewallLocalCIDR]), | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if caSha == "" && caName == "" { | 
					
						
							|  |  |  | 		if fc.Any == nil { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 			fc.Any = fr() | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		return fc.Any.addRule(f, groups, host, ip, localIp) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if caSha != "" { | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 		if _, ok := fc.CAShas[caSha]; !ok { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 			fc.CAShas[caSha] = fr() | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		err := fc.CAShas[caSha].addRule(f, groups, host, ip, localIp) | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if caName != "" { | 
					
						
							|  |  |  | 		if _, ok := fc.CANames[caName]; !ok { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 			fc.CANames[caName] = fr() | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		err := fc.CANames[caName].addRule(f, groups, host, ip, localIp) | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | func (fc *FirewallCA) match(p firewall.Packet, c *cert.CachedCertificate, caPool *cert.CAPool) bool { | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 	if fc == nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-18 21:23:34 -08:00
										 |  |  | 	if fc.Any.match(p, c) { | 
					
						
							|  |  |  | 		return true | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 	if t, ok := fc.CAShas[c.Certificate.Issuer()]; ok { | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 		if t.match(p, c) { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 	s, err := caPool.GetCAForCert(c.Certificate) | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 	return fc.CANames[s.Certificate.Name()].match(p, c) | 
					
						
							| 
									
										
										
										
											2019-12-17 23:36:12 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | func (fr *FirewallRule) addRule(f *Firewall, groups []string, host string, ip, localCIDR netip.Prefix) error { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	flc := func() *firewallLocalCIDR { | 
					
						
							|  |  |  | 		return &firewallLocalCIDR{ | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 			LocalCIDR: new(bart.Table[struct{}]), | 
					
						
							| 
									
										
										
										
											2024-01-23 16:02:10 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	if fr.isAny(groups, host, ip) { | 
					
						
							|  |  |  | 		if fr.Any == nil { | 
					
						
							|  |  |  | 			fr.Any = flc() | 
					
						
							| 
									
										
										
										
											2024-01-23 16:02:10 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		return fr.Any.addRule(f, localCIDR) | 
					
						
							| 
									
										
										
										
											2024-01-23 16:02:10 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	if len(groups) > 0 { | 
					
						
							|  |  |  | 		nlc := flc() | 
					
						
							|  |  |  | 		err := nlc.addRule(f, localCIDR) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-23 16:02:10 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		fr.Groups = append(fr.Groups, &firewallGroups{ | 
					
						
							|  |  |  | 			Groups:    groups, | 
					
						
							|  |  |  | 			LocalCIDR: nlc, | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	if host != "" { | 
					
						
							|  |  |  | 		nlc := fr.Hosts[host] | 
					
						
							|  |  |  | 		if nlc == nil { | 
					
						
							|  |  |  | 			nlc = flc() | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		err := nlc.addRule(f, localCIDR) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		fr.Hosts[host] = nlc | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 	if ip.IsValid() { | 
					
						
							|  |  |  | 		nlc, _ := fr.CIDR.Get(ip) | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		if nlc == nil { | 
					
						
							|  |  |  | 			nlc = flc() | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		err := nlc.addRule(f, localCIDR) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 		fr.CIDR.Insert(ip, nlc) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | func (fr *FirewallRule) isAny(groups []string, host string, ip netip.Prefix) bool { | 
					
						
							|  |  |  | 	if len(groups) == 0 && host == "" && !ip.IsValid() { | 
					
						
							| 
									
										
										
										
											2019-12-18 21:23:34 -08:00
										 |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	for _, group := range groups { | 
					
						
							|  |  |  | 		if group == "any" { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if host == "any" { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 	if ip.IsValid() && ip.Bits() == 0 { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | func (fr *FirewallRule) match(p firewall.Packet, c *cert.CachedCertificate) bool { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	if fr == nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Shortcut path for if groups, hosts, or cidr contained an `any` | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	if fr.Any.match(p, c) { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Need any of group, host, or cidr to match | 
					
						
							|  |  |  | 	for _, sg := range fr.Groups { | 
					
						
							|  |  |  | 		found := false | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		for _, g := range sg.Groups { | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 			if _, ok := c.InvertedGroups[g]; !ok { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 				found = false | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			found = true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 		if found && sg.LocalCIDR.match(p, c) { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if fr.Hosts != nil { | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 		if flc, ok := fr.Hosts[c.Certificate.Name()]; ok { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 			if flc.match(p, c) { | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 13:26:29 -06:00
										 |  |  | 	for _, v := range fr.CIDR.Supernets(netip.PrefixFrom(p.RemoteAddr, p.RemoteAddr.BitLen())) { | 
					
						
							|  |  |  | 		if v.match(p, c) { | 
					
						
							|  |  |  | 			return true | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-03-06 13:26:29 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | func (flc *firewallLocalCIDR) addRule(f *Firewall, localIp netip.Prefix) error { | 
					
						
							|  |  |  | 	if !localIp.IsValid() { | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | 		if !f.hasUnsafeNetworks || f.defaultLocalCIDRAny { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 			flc.Any = true | 
					
						
							|  |  |  | 			return nil | 
					
						
							| 
									
										
										
										
											2023-11-02 17:05:08 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-02-15 11:44:05 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 		for _, network := range f.assignedNetworks { | 
					
						
							|  |  |  | 			flc.LocalCIDR.Insert(network, struct{}{}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 	} else if localIp.Bits() == 0 { | 
					
						
							| 
									
										
										
										
											2024-03-28 16:17:12 -04:00
										 |  |  | 		flc.Any = true | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 10:18:56 -05:00
										 |  |  | 	flc.LocalCIDR.Insert(localIp, struct{}{}) | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 18:00:22 -05:00
										 |  |  | func (flc *firewallLocalCIDR) match(p firewall.Packet, c *cert.CachedCertificate) bool { | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	if flc == nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if flc.Any { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 11:28:26 -06:00
										 |  |  | 	_, ok := flc.LocalCIDR.Lookup(p.LocalAddr) | 
					
						
							| 
									
										
										
										
											2024-01-29 15:30:52 -06:00
										 |  |  | 	return ok | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type rule struct { | 
					
						
							| 
									
										
										
										
											2023-05-09 16:37:23 +01:00
										 |  |  | 	Port      string | 
					
						
							|  |  |  | 	Code      string | 
					
						
							|  |  |  | 	Proto     string | 
					
						
							|  |  |  | 	Host      string | 
					
						
							|  |  |  | 	Group     string | 
					
						
							|  |  |  | 	Groups    []string | 
					
						
							|  |  |  | 	Cidr      string | 
					
						
							|  |  |  | 	LocalCidr string | 
					
						
							|  |  |  | 	CAName    string | 
					
						
							|  |  |  | 	CASha     string | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-26 09:46:30 -05:00
										 |  |  | func convertRule(l *logrus.Logger, p interface{}, table string, i int) (rule, error) { | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	r := rule{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	m, ok := p.(map[interface{}]interface{}) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return r, errors.New("could not parse rule") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	toString := func(k string, m map[interface{}]interface{}) string { | 
					
						
							|  |  |  | 		v, ok := m[k] | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			return "" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return fmt.Sprintf("%v", v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	r.Port = toString("port", m) | 
					
						
							|  |  |  | 	r.Code = toString("code", m) | 
					
						
							|  |  |  | 	r.Proto = toString("proto", m) | 
					
						
							|  |  |  | 	r.Host = toString("host", m) | 
					
						
							|  |  |  | 	r.Cidr = toString("cidr", m) | 
					
						
							| 
									
										
										
										
											2023-05-09 16:37:23 +01:00
										 |  |  | 	r.LocalCidr = toString("local_cidr", m) | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	r.CAName = toString("ca_name", m) | 
					
						
							|  |  |  | 	r.CASha = toString("ca_sha", m) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-13 13:45:21 -08:00
										 |  |  | 	// Make sure group isn't an array | 
					
						
							|  |  |  | 	if v, ok := m["group"].([]interface{}); ok { | 
					
						
							|  |  |  | 		if len(v) > 1 { | 
					
						
							|  |  |  | 			return r, errors.New("group should contain a single value, an array with more than one entry was provided") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		l.Warnf("%s rule #%v; group was an array with a single value, converting to simple value", table, i) | 
					
						
							|  |  |  | 		m["group"] = v[0] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	r.Group = toString("group", m) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 	if rg, ok := m["groups"]; ok { | 
					
						
							|  |  |  | 		switch reflect.TypeOf(rg).Kind() { | 
					
						
							|  |  |  | 		case reflect.Slice: | 
					
						
							|  |  |  | 			v := reflect.ValueOf(rg) | 
					
						
							|  |  |  | 			r.Groups = make([]string, v.Len()) | 
					
						
							|  |  |  | 			for i := 0; i < v.Len(); i++ { | 
					
						
							|  |  |  | 				r.Groups[i] = v.Index(i).Interface().(string) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case reflect.String: | 
					
						
							|  |  |  | 			r.Groups = []string{rg.(string)} | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			r.Groups = []string{fmt.Sprintf("%v", rg)} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return r, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func parsePort(s string) (startPort, endPort int32, err error) { | 
					
						
							|  |  |  | 	if s == "any" { | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 		startPort = firewall.PortAny | 
					
						
							|  |  |  | 		endPort = firewall.PortAny | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	} else if s == "fragment" { | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 		startPort = firewall.PortFragment | 
					
						
							|  |  |  | 		endPort = firewall.PortFragment | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	} else if strings.Contains(s, `-`) { | 
					
						
							|  |  |  | 		sPorts := strings.SplitN(s, `-`, 2) | 
					
						
							|  |  |  | 		sPorts[0] = strings.Trim(sPorts[0], " ") | 
					
						
							|  |  |  | 		sPorts[1] = strings.Trim(sPorts[1], " ") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if len(sPorts) != 2 || sPorts[0] == "" || sPorts[1] == "" { | 
					
						
							|  |  |  | 			return 0, 0, fmt.Errorf("appears to be a range but could not be parsed; `%s`", s) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rStartPort, err := strconv.Atoi(sPorts[0]) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return 0, 0, fmt.Errorf("beginning range was not a number; `%s`", sPorts[0]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rEndPort, err := strconv.Atoi(sPorts[1]) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return 0, 0, fmt.Errorf("ending range was not a number; `%s`", sPorts[1]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		startPort = int32(rStartPort) | 
					
						
							|  |  |  | 		endPort = int32(rEndPort) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 20:54:04 -05:00
										 |  |  | 		if startPort == firewall.PortAny { | 
					
						
							|  |  |  | 			endPort = firewall.PortAny | 
					
						
							| 
									
										
										
										
											2019-11-19 17:00:20 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		rPort, err := strconv.Atoi(s) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return 0, 0, fmt.Errorf("was not a number; `%s`", s) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		startPort = int32(rPort) | 
					
						
							|  |  |  | 		endPort = startPort | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } |