Add session firewall (extra security for controlling traffic flow to/from a given node)

This commit is contained in:
Neil Alexander 2018-10-07 17:13:41 +01:00
parent 401960e17e
commit 2e2c58bfef
No known key found for this signature in database
GPG Key ID: A02A2019A2BB0944
6 changed files with 103 additions and 2 deletions

View File

@ -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."`
//Net NetConfig `comment:"Extended options for connecting to peers over other networks."`
}
@ -24,3 +25,11 @@ 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."`
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."`
}

View File

@ -107,6 +107,11 @@ 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)
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

View File

@ -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{}
}

View File

@ -6,6 +6,7 @@ package yggdrasil
import (
"bytes"
"encoding/hex"
"time"
)
@ -107,6 +108,12 @@ 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
sessionFirewallWhitelist []string
sessionFirewallBlacklist []string
}
// Initializes the session struct.
@ -121,6 +128,77 @@ 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) {
ss.sessionFirewallAllowsDirect = allowsDirect
ss.sessionFirewallAllowsRemote = allowsRemote
}
// 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) 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
}
}
}
// 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]
@ -311,6 +389,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 ss.sessionFirewallEnabled {
if !ss.isSessionAllowed(&ping.SendPermPub) {
return
}
}
if !isIn || sinfo.timedout() {
if isIn {
sinfo.close()

View File

@ -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()

View File

@ -66,6 +66,9 @@ func generateConfig(isAutoconf bool) *nodeConfig {
cfg.IfName = defaults.GetDefaults().DefaultIfName
cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU
cfg.IfTAPMode = defaults.GetDefaults().DefaultIfTAPMode
cfg.SessionFirewall.Enable = false
cfg.SessionFirewall.AllowFromDirect = true
cfg.SessionFirewall.AllowFromRemote = true
return &cfg
}