mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2024-12-04 07:25:17 +00:00
Merge pull request #187 from neilalexander/sessionfirewall
Add session firewall
This commit is contained in:
commit
ba8af20817
@ -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."`
|
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."`
|
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."`
|
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."`
|
//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."`
|
Tor TorConfig `comment:"Experimental options for configuring peerings over Tor."`
|
||||||
I2P I2PConfig `comment:"Experimental options for configuring peerings over I2P."`
|
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
|
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 {
|
if err := c.router.start(); err != nil {
|
||||||
c.log.Println("Failed to start router")
|
c.log.Println("Failed to start router")
|
||||||
return err
|
return err
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
type peers struct {
|
type peers struct {
|
||||||
core *Core
|
core *Core
|
||||||
mutex sync.Mutex // Synchronize writes to atomic
|
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
|
authMutex sync.RWMutex
|
||||||
allowedEncryptionPublicKeys map[boxPubKey]struct{}
|
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)
|
sinfo, isIn := s.core.sessions.getByTheirPerm(&res.Key)
|
||||||
if !isIn {
|
if !isIn {
|
||||||
sinfo = s.core.sessions.createSession(&res.Key)
|
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)
|
_, isIn := s.core.sessions.getByTheirPerm(&res.Key)
|
||||||
if !isIn {
|
if !isIn {
|
||||||
panic("This should never happen")
|
panic("This should never happen")
|
||||||
|
@ -6,6 +6,7 @@ package yggdrasil
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -107,6 +108,13 @@ type sessions struct {
|
|||||||
byTheirPerm map[boxPubKey]*handle
|
byTheirPerm map[boxPubKey]*handle
|
||||||
addrToPerm map[address]*boxPubKey
|
addrToPerm map[address]*boxPubKey
|
||||||
subnetToPerm map[subnet]*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.
|
// Initializes the session struct.
|
||||||
@ -121,6 +129,84 @@ func (ss *sessions) init(core *Core) {
|
|||||||
ss.lastCleanup = time.Now()
|
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.
|
// Gets the session corresponding to a given handle.
|
||||||
func (ss *sessions) getSessionForHandle(handle *handle) (*sessionInfo, bool) {
|
func (ss *sessions) getSessionForHandle(handle *handle) (*sessionInfo, bool) {
|
||||||
sinfo, isIn := ss.sinfos[*handle]
|
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.
|
// 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).
|
// This includse initializing session info to sane defaults (e.g. lowest supported MTU).
|
||||||
func (ss *sessions) createSession(theirPermKey *boxPubKey) *sessionInfo {
|
func (ss *sessions) createSession(theirPermKey *boxPubKey) *sessionInfo {
|
||||||
|
if ss.sessionFirewallEnabled {
|
||||||
|
if !ss.isSessionAllowed(theirPermKey, true) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
sinfo := sessionInfo{}
|
sinfo := sessionInfo{}
|
||||||
sinfo.core = ss.core
|
sinfo.core = ss.core
|
||||||
sinfo.theirPermPub = *theirPermKey
|
sinfo.theirPermPub = *theirPermKey
|
||||||
@ -311,6 +402,12 @@ func (ss *sessions) sendPingPong(sinfo *sessionInfo, isPong bool) {
|
|||||||
func (ss *sessions) handlePing(ping *sessionPing) {
|
func (ss *sessions) handlePing(ping *sessionPing) {
|
||||||
// Get the corresponding session (or create a new session)
|
// Get the corresponding session (or create a new session)
|
||||||
sinfo, isIn := ss.getByTheirPerm(&ping.SendPermPub)
|
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.timedout() {
|
||||||
if isIn {
|
if isIn {
|
||||||
sinfo.close()
|
sinfo.close()
|
||||||
|
@ -66,6 +66,9 @@ func generateConfig(isAutoconf bool) *nodeConfig {
|
|||||||
cfg.IfName = defaults.GetDefaults().DefaultIfName
|
cfg.IfName = defaults.GetDefaults().DefaultIfName
|
||||||
cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU
|
cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU
|
||||||
cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode
|
cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode
|
||||||
|
cfg.SessionFirewall.Enable = false
|
||||||
|
cfg.SessionFirewall.AllowFromDirect = true
|
||||||
|
cfg.SessionFirewall.AllowFromRemote = true
|
||||||
|
|
||||||
return &cfg
|
return &cfg
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user