From 907986f2009f649a27df4f73676975a59319a2d2 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Tue, 11 Jun 2019 12:50:01 +0100 Subject: [PATCH] Implement session firewall as gatekeeper func in cmd/yggdrasil --- cmd/yggdrasil/main.go | 85 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 10 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index 5dba3a51..fb6cccbd 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "encoding/hex" "encoding/json" "flag" "fmt" @@ -20,22 +21,21 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/admin" "github.com/yggdrasil-network/yggdrasil-go/src/config" + "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/multicast" "github.com/yggdrasil-network/yggdrasil-go/src/tuntap" "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" ) -type nodeConfig = config.NodeConfig -type Core = yggdrasil.Core - type node struct { - core Core + core yggdrasil.Core + state *config.NodeState tuntap tuntap.TunAdapter multicast multicast.Multicast admin admin.AdminSocket } -func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *nodeConfig { +func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *config.NodeConfig { // Use a configuration file. If -useconf, the configuration will be read // from stdin. If -useconffile, the configuration will be read from the // filesystem. @@ -116,7 +116,7 @@ func main() { logging := flag.String("logging", "info,warn,error", "comma-separated list of logging levels to enable") flag.Parse() - var cfg *nodeConfig + var cfg *config.NodeConfig var err error switch { case *version: @@ -181,18 +181,20 @@ func main() { n := node{} // Now start Yggdrasil - this starts the DHT, router, switch and other core // components needed for Yggdrasil to operate - state, err := n.core.Start(cfg, logger) + n.state, err = n.core.Start(cfg, logger) if err != nil { logger.Errorln("An error occurred during startup") panic(err) } + // Register the session firewall gatekeeper function + n.core.SetSessionGatekeeper(n.sessionFirewall) // Start the admin socket - n.admin.Init(&n.core, state, logger, nil) + n.admin.Init(&n.core, n.state, logger, nil) if err := n.admin.Start(); err != nil { logger.Errorln("An error occurred starting admin socket:", err) } // Start the multicast interface - n.multicast.Init(&n.core, state, logger, nil) + n.multicast.Init(&n.core, n.state, logger, nil) if err := n.multicast.Start(); err != nil { logger.Errorln("An error occurred starting multicast:", err) } @@ -200,7 +202,7 @@ func main() { // Start the TUN/TAP interface if listener, err := n.core.ConnListen(); err == nil { if dialer, err := n.core.ConnDialer(); err == nil { - n.tuntap.Init(state, logger, listener, dialer) + n.tuntap.Init(n.state, logger, listener, dialer) if err := n.tuntap.Start(); err != nil { logger.Errorln("An error occurred starting TUN/TAP:", err) } @@ -251,3 +253,66 @@ func main() { } exit: } + +func (n *node) sessionFirewall(pubkey *crypto.BoxPubKey, initiator bool) bool { + n.state.Mutex.RLock() + defer n.state.Mutex.RUnlock() + + // Allow by default if the session firewall is disabled + if !n.state.Current.SessionFirewall.Enable { + return true + } + + // Prepare for checking whitelist/blacklist + var box crypto.BoxPubKey + // Reject blacklisted nodes + for _, b := range n.state.Current.SessionFirewall.BlacklistEncryptionPublicKeys { + key, err := hex.DecodeString(b) + if err == nil { + copy(box[:crypto.BoxPubKeyLen], key) + if box == *pubkey { + return false + } + } + } + + // Allow whitelisted nodes + for _, b := range n.state.Current.SessionFirewall.WhitelistEncryptionPublicKeys { + key, err := hex.DecodeString(b) + if err == nil { + copy(box[:crypto.BoxPubKeyLen], key) + if box == *pubkey { + return true + } + } + } + + // Allow outbound sessions if appropriate + if n.state.Current.SessionFirewall.AlwaysAllowOutbound { + if initiator { + return true + } + } + + // Look and see if the pubkey is that of a direct peer + var isDirectPeer bool + for _, peer := range n.core.GetPeers() { + if peer.PublicKey == *pubkey { + isDirectPeer = true + break + } + } + + // Allow direct peers if appropriate + if n.state.Current.SessionFirewall.AllowFromDirect && isDirectPeer { + return true + } + + // Allow remote nodes if appropriate + if n.state.Current.SessionFirewall.AllowFromRemote && !isDirectPeer { + return true + } + + // Finally, default-deny if not matching any of the above rules + return false +}