diff --git a/src/yggdrasil/config/config.go b/src/yggdrasil/config/config.go index ef5f7c2..c08cab6 100644 --- a/src/yggdrasil/config/config.go +++ b/src/yggdrasil/config/config.go @@ -30,6 +30,7 @@ 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."` } diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 64dff63..015147c 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -108,7 +108,11 @@ func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) error { } c.sessions.setSessionFirewallState(nc.SessionFirewall.Enable) - c.sessions.setSessionFirewallDefaults(nc.SessionFirewall.AllowFromDirect, nc.SessionFirewall.AllowFromRemote) + c.sessions.setSessionFirewallDefaults( + nc.SessionFirewall.AllowFromDirect, + nc.SessionFirewall.AllowFromRemote, + nc.SessionFirewall.AlwaysAllowOutbound, + ) c.sessions.setSessionFirewallWhitelist(nc.SessionFirewall.WhitelistEncryptionPublicKeys) c.sessions.setSessionFirewallBlacklist(nc.SessionFirewall.BlacklistEncryptionPublicKeys) diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 2928faa..1b72a63 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -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") diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index 1b8d1cb..0bc27a1 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -109,11 +109,12 @@ type sessions struct { addrToPerm map[address]*boxPubKey subnetToPerm map[subnet]*boxPubKey // Options from the session firewall - sessionFirewallEnabled bool - sessionFirewallAllowsDirect bool - sessionFirewallAllowsRemote bool - sessionFirewallWhitelist []string - sessionFirewallBlacklist []string + sessionFirewallEnabled bool + sessionFirewallAllowsDirect bool + sessionFirewallAllowsRemote bool + sessionFirewallAlwaysAllowsOutbound bool + sessionFirewallWhitelist []string + sessionFirewallBlacklist []string } // Initializes the session struct. @@ -135,9 +136,10 @@ func (ss *sessions) setSessionFirewallState(enabled bool) { // 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) { +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. @@ -152,7 +154,7 @@ func (ss *sessions) setSessionFirewallBlacklist(blacklist []string) { // Determines whether the session with a given publickey is allowed based on // session firewall rules. -func (ss *sessions) isSessionAllowed(pubkey *boxPubKey) bool { +func (ss *sessions) isSessionAllowed(pubkey *boxPubKey, initiator bool) bool { // Allow by default if the session firewall is disabled if !ss.sessionFirewallEnabled { return true @@ -179,6 +181,12 @@ func (ss *sessions) isSessionAllowed(pubkey *boxPubKey) bool { } } } + // 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) { @@ -252,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 @@ -391,7 +404,7 @@ func (ss *sessions) handlePing(ping *sessionPing) { sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub) // Check the session firewall if !isIn && ss.sessionFirewallEnabled { - if !ss.isSessionAllowed(&ping.SendPermPub) { + if !ss.isSessionAllowed(&ping.SendPermPub, false) { return } }