mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 03:05:07 +03:00 
			
		
		
		
	Merge pull request #187 from neilalexander/sessionfirewall
Add session firewall
This commit is contained in:
		
						commit
						ba8af20817
					
				
					 7 changed files with 125 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -16,6 +16,7 @@ type NodeConfig struct {
 | 
			
		|||
	IfName                      string              `comment:"Local network interface name for TUN/TAP adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN/TAP."`
 | 
			
		||||
	IfTAPMode                   bool                `comment:"Set local network interface to TAP mode rather than TUN mode if\nsupported by your platform - option will be ignored if not."`
 | 
			
		||||
	IfMTU                       int                 `comment:"Maximux Transmission Unit (MTU) size for your local TUN/TAP interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
 | 
			
		||||
	SessionFirewall             SessionFirewall     `comment:"The session firewall controls who can send/receive network traffic\nto/from. This is useful if you want to protect this node without\nresorting to using a real firewall. This does not affect traffic\nbeing routed via this node to somewhere else. Rules are prioritised as\nfollows: whitelist, blacklist, always allow outgoing, direct, remote."`
 | 
			
		||||
	//Net                         NetConfig `comment:"Extended options for connecting to peers over other networks."`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,3 +25,12 @@ type NetConfig struct {
 | 
			
		|||
	Tor TorConfig `comment:"Experimental options for configuring peerings over Tor."`
 | 
			
		||||
	I2P I2PConfig `comment:"Experimental options for configuring peerings over I2P."`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SessionFirewall struct {
 | 
			
		||||
	Enable                        bool     `comment:"Enable or disable the session firewall. If disabled, network traffic\nfrom any node will be allowed. If enabled, the below rules apply."`
 | 
			
		||||
	AllowFromDirect               bool     `comment:"Allow network traffic from directly connected peers."`
 | 
			
		||||
	AllowFromRemote               bool     `comment:"Allow network traffic from remote nodes on the network that you are\nnot directly peered with."`
 | 
			
		||||
	AlwaysAllowOutbound           bool     `comment:"Allow outbound network traffic regardless of AllowFromDirect or\nAllowFromRemote. This does allow a remote node to send unsolicited\ntraffic back to you for the length of the session."`
 | 
			
		||||
	WhitelistEncryptionPublicKeys []string `comment:"List of public keys from which network traffic is always accepted,\nregardless of AllowFromDirect or AllowFromRemote."`
 | 
			
		||||
	BlacklistEncryptionPublicKeys []string `comment:"List of public keys from which network traffic is always rejected,\nregardless of the whitelist, AllowFromDirect or AllowFromRemote."`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -107,6 +107,15 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error {
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.sessions.setSessionFirewallState(nc.SessionFirewall.Enable)
 | 
			
		||||
	c.sessions.setSessionFirewallDefaults(
 | 
			
		||||
		nc.SessionFirewall.AllowFromDirect,
 | 
			
		||||
		nc.SessionFirewall.AllowFromRemote,
 | 
			
		||||
		nc.SessionFirewall.AlwaysAllowOutbound,
 | 
			
		||||
	)
 | 
			
		||||
	c.sessions.setSessionFirewallWhitelist(nc.SessionFirewall.WhitelistEncryptionPublicKeys)
 | 
			
		||||
	c.sessions.setSessionFirewallBlacklist(nc.SessionFirewall.BlacklistEncryptionPublicKeys)
 | 
			
		||||
 | 
			
		||||
	if err := c.router.start(); err != nil {
 | 
			
		||||
		c.log.Println("Failed to start router")
 | 
			
		||||
		return err
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ import (
 | 
			
		|||
type peers struct {
 | 
			
		||||
	core                        *Core
 | 
			
		||||
	mutex                       sync.Mutex   // Synchronize writes to atomic
 | 
			
		||||
	ports                       atomic.Value //map[Port]*peer, use CoW semantics
 | 
			
		||||
	ports                       atomic.Value //map[switchPort]*peer, use CoW semantics
 | 
			
		||||
	authMutex                   sync.RWMutex
 | 
			
		||||
	allowedEncryptionPublicKeys map[boxPubKey]struct{}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -184,6 +184,10 @@ func (s *searches) checkDHTRes(info *searchInfo, res *dhtRes) bool {
 | 
			
		|||
	sinfo, isIn := s.core.sessions.getByTheirPerm(&res.Key)
 | 
			
		||||
	if !isIn {
 | 
			
		||||
		sinfo = s.core.sessions.createSession(&res.Key)
 | 
			
		||||
		if sinfo == nil {
 | 
			
		||||
			// nil if the DHT search finished but the session wasn't allowed
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		_, isIn := s.core.sessions.getByTheirPerm(&res.Key)
 | 
			
		||||
		if !isIn {
 | 
			
		||||
			panic("This should never happen")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ package yggdrasil
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -107,6 +108,13 @@ type sessions struct {
 | 
			
		|||
	byTheirPerm  map[boxPubKey]*handle
 | 
			
		||||
	addrToPerm   map[address]*boxPubKey
 | 
			
		||||
	subnetToPerm map[subnet]*boxPubKey
 | 
			
		||||
	// Options from the session firewall
 | 
			
		||||
	sessionFirewallEnabled              bool
 | 
			
		||||
	sessionFirewallAllowsDirect         bool
 | 
			
		||||
	sessionFirewallAllowsRemote         bool
 | 
			
		||||
	sessionFirewallAlwaysAllowsOutbound bool
 | 
			
		||||
	sessionFirewallWhitelist            []string
 | 
			
		||||
	sessionFirewallBlacklist            []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Initializes the session struct.
 | 
			
		||||
| 
						 | 
				
			
			@ -121,6 +129,84 @@ func (ss *sessions) init(core *Core) {
 | 
			
		|||
	ss.lastCleanup = time.Now()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Enable or disable the session firewall
 | 
			
		||||
func (ss *sessions) setSessionFirewallState(enabled bool) {
 | 
			
		||||
	ss.sessionFirewallEnabled = enabled
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set the session firewall defaults (first parameter is whether to allow
 | 
			
		||||
// sessions from direct peers, second is whether to allow from remote nodes).
 | 
			
		||||
func (ss *sessions) setSessionFirewallDefaults(allowsDirect bool, allowsRemote bool, alwaysAllowsOutbound bool) {
 | 
			
		||||
	ss.sessionFirewallAllowsDirect = allowsDirect
 | 
			
		||||
	ss.sessionFirewallAllowsRemote = allowsRemote
 | 
			
		||||
	ss.sessionFirewallAlwaysAllowsOutbound = alwaysAllowsOutbound
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set the session firewall whitelist - nodes always allowed to open sessions.
 | 
			
		||||
func (ss *sessions) setSessionFirewallWhitelist(whitelist []string) {
 | 
			
		||||
	ss.sessionFirewallWhitelist = whitelist
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set the session firewall blacklist - nodes never allowed to open sessions.
 | 
			
		||||
func (ss *sessions) setSessionFirewallBlacklist(blacklist []string) {
 | 
			
		||||
	ss.sessionFirewallBlacklist = blacklist
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Determines whether the session with a given publickey is allowed based on
 | 
			
		||||
// session firewall rules.
 | 
			
		||||
func (ss *sessions) isSessionAllowed(pubkey *boxPubKey, initiator bool) bool {
 | 
			
		||||
	// Allow by default if the session firewall is disabled
 | 
			
		||||
	if !ss.sessionFirewallEnabled {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	// Prepare for checking whitelist/blacklist
 | 
			
		||||
	var box boxPubKey
 | 
			
		||||
	// Reject blacklisted nodes
 | 
			
		||||
	for _, b := range ss.sessionFirewallBlacklist {
 | 
			
		||||
		key, err := hex.DecodeString(b)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			copy(box[:boxPubKeyLen], key)
 | 
			
		||||
			if box == *pubkey {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Allow whitelisted nodes
 | 
			
		||||
	for _, b := range ss.sessionFirewallWhitelist {
 | 
			
		||||
		key, err := hex.DecodeString(b)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			copy(box[:boxPubKeyLen], key)
 | 
			
		||||
			if box == *pubkey {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Allow outbound sessions if appropriate
 | 
			
		||||
	if ss.sessionFirewallAlwaysAllowsOutbound {
 | 
			
		||||
		if initiator {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Look and see if the pubkey is that of a direct peer
 | 
			
		||||
	var isDirectPeer bool
 | 
			
		||||
	for _, peer := range ss.core.peers.ports.Load().(map[switchPort]*peer) {
 | 
			
		||||
		if peer.box == *pubkey {
 | 
			
		||||
			isDirectPeer = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Allow direct peers if appropriate
 | 
			
		||||
	if ss.sessionFirewallAllowsDirect && isDirectPeer {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	// Allow remote nodes if appropriate
 | 
			
		||||
	if ss.sessionFirewallAllowsRemote && !isDirectPeer {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	// Finally, default-deny if not matching any of the above rules
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Gets the session corresponding to a given handle.
 | 
			
		||||
func (ss *sessions) getSessionForHandle(handle *handle) (*sessionInfo, bool) {
 | 
			
		||||
	sinfo, isIn := ss.sinfos[*handle]
 | 
			
		||||
| 
						 | 
				
			
			@ -174,6 +260,11 @@ func (ss *sessions) getByTheirSubnet(snet *subnet) (*sessionInfo, bool) {
 | 
			
		|||
// Creates a new session and lazily cleans up old/timedout existing sessions.
 | 
			
		||||
// This includse initializing session info to sane defaults (e.g. lowest supported MTU).
 | 
			
		||||
func (ss *sessions) createSession(theirPermKey *boxPubKey) *sessionInfo {
 | 
			
		||||
	if ss.sessionFirewallEnabled {
 | 
			
		||||
		if !ss.isSessionAllowed(theirPermKey, true) {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	sinfo := sessionInfo{}
 | 
			
		||||
	sinfo.core = ss.core
 | 
			
		||||
	sinfo.theirPermPub = *theirPermKey
 | 
			
		||||
| 
						 | 
				
			
			@ -311,6 +402,12 @@ func (ss *sessions) sendPingPong(sinfo *sessionInfo, isPong bool) {
 | 
			
		|||
func (ss *sessions) handlePing(ping *sessionPing) {
 | 
			
		||||
	// Get the corresponding session (or create a new session)
 | 
			
		||||
	sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub)
 | 
			
		||||
	// Check the session firewall
 | 
			
		||||
	if !isIn && ss.sessionFirewallEnabled {
 | 
			
		||||
		if !ss.isSessionAllowed(&ping.SendPermPub, false) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !isIn || sinfo.timedout() {
 | 
			
		||||
		if isIn {
 | 
			
		||||
			sinfo.close()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -164,7 +164,7 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) {
 | 
			
		|||
				if err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				} else {
 | 
			
		||||
					if ief.Flags & net.FlagUp == 0 {
 | 
			
		||||
					if ief.Flags&net.FlagUp == 0 {
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
					addrs, err := ief.Addrs()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue