From fdf300a1ffab2139a19ec13610fff40a669f9958 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 20:26:39 +0000 Subject: [PATCH 1/3] Handle AllowedEncryptionPublicKeys internally --- cmd/yggdrasil/main.go | 5 ---- src/yggdrasil/admin.go | 25 ++++------------- src/yggdrasil/peer.go | 63 +++++++++++++++++++++++------------------- 3 files changed, 40 insertions(+), 53 deletions(-) diff --git a/cmd/yggdrasil/main.go b/cmd/yggdrasil/main.go index c3add0c..f11bbc0 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/yggdrasil/main.go @@ -227,11 +227,6 @@ func main() { logger.Println("An error occurred during startup") panic(err) } - // Check to see if any allowed encryption keys were provided in the config. - // If they were then set them now. - for _, pBoxStr := range cfg.AllowedEncryptionPublicKeys { - n.core.AddAllowedEncryptionPublicKey(pBoxStr) - } // The Stop function ensures that the TUN/TAP adapter is correctly shut down // before the program exits. defer func() { diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 5524fe2..f601776 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -765,35 +765,20 @@ func (a *admin) getData_getSessions() []admin_nodeInfo { // getAllowedEncryptionPublicKeys returns the public keys permitted for incoming peer connections. func (a *admin) getAllowedEncryptionPublicKeys() []string { - pubs := a.core.peers.getAllowedEncryptionPublicKeys() - var out []string - for _, pub := range pubs { - out = append(out, hex.EncodeToString(pub[:])) - } - return out + return a.core.peers.getAllowedEncryptionPublicKeys() } // addAllowedEncryptionPublicKey whitelists a key for incoming peer connections. func (a *admin) addAllowedEncryptionPublicKey(bstr string) (err error) { - boxBytes, err := hex.DecodeString(bstr) - if err == nil { - var box crypto.BoxPubKey - copy(box[:], boxBytes) - a.core.peers.addAllowedEncryptionPublicKey(&box) - } - return + a.core.peers.addAllowedEncryptionPublicKey(bstr) + return nil } // removeAllowedEncryptionPublicKey removes a key from the whitelist for incoming peer connections. // If none are set, an empty list permits all incoming connections. func (a *admin) removeAllowedEncryptionPublicKey(bstr string) (err error) { - boxBytes, err := hex.DecodeString(bstr) - if err == nil { - var box crypto.BoxPubKey - copy(box[:], boxBytes) - a.core.peers.removeAllowedEncryptionPublicKey(&box) - } - return + a.core.peers.removeAllowedEncryptionPublicKey(bstr) + return nil } // Send a DHT ping to the node with the provided key and coords, optionally looking up the specified target NodeID. diff --git a/src/yggdrasil/peer.go b/src/yggdrasil/peer.go index c83504f..2cd1afe 100644 --- a/src/yggdrasil/peer.go +++ b/src/yggdrasil/peer.go @@ -5,6 +5,7 @@ package yggdrasil // Live code should be better commented import ( + "encoding/hex" "sync" "sync/atomic" "time" @@ -18,12 +19,10 @@ import ( // In most cases, this involves passing the packet to the handler for outgoing traffic to another peer. // In other cases, it's link protocol traffic used to build the spanning tree, in which case this checks signatures and passes the message along to the switch. type peers struct { - core *Core - reconfigure chan chan error - mutex sync.Mutex // Synchronize writes to atomic - ports atomic.Value //map[switchPort]*peer, use CoW semantics - authMutex sync.RWMutex - allowedEncryptionPublicKeys map[crypto.BoxPubKey]struct{} + core *Core + reconfigure chan chan error + mutex sync.Mutex // Synchronize writes to atomic + ports atomic.Value //map[switchPort]*peer, use CoW semantics } // Initializes the peers struct. @@ -39,40 +38,48 @@ func (ps *peers) init(c *Core) { e <- nil } }() - ps.allowedEncryptionPublicKeys = make(map[crypto.BoxPubKey]struct{}) } -// Returns true if an incoming peer connection to a key is allowed, either because the key is in the whitelist or because the whitelist is empty. +// Returns true if an incoming peer connection to a key is allowed, either +// because the key is in the whitelist or because the whitelist is empty. func (ps *peers) isAllowedEncryptionPublicKey(box *crypto.BoxPubKey) bool { - ps.authMutex.RLock() - defer ps.authMutex.RUnlock() - _, isIn := ps.allowedEncryptionPublicKeys[*box] - return isIn || len(ps.allowedEncryptionPublicKeys) == 0 + boxstr := hex.EncodeToString(box[:]) + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + for _, v := range ps.core.config.AllowedEncryptionPublicKeys { + if v == boxstr { + return true + } + } + return len(ps.core.config.AllowedEncryptionPublicKeys) == 0 } // Adds a key to the whitelist. -func (ps *peers) addAllowedEncryptionPublicKey(box *crypto.BoxPubKey) { - ps.authMutex.Lock() - defer ps.authMutex.Unlock() - ps.allowedEncryptionPublicKeys[*box] = struct{}{} +func (ps *peers) addAllowedEncryptionPublicKey(box string) { + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + ps.core.config.AllowedEncryptionPublicKeys = + append(ps.core.config.AllowedEncryptionPublicKeys, box) } // Removes a key from the whitelist. -func (ps *peers) removeAllowedEncryptionPublicKey(box *crypto.BoxPubKey) { - ps.authMutex.Lock() - defer ps.authMutex.Unlock() - delete(ps.allowedEncryptionPublicKeys, *box) +func (ps *peers) removeAllowedEncryptionPublicKey(box string) { + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + for k, v := range ps.core.config.AllowedEncryptionPublicKeys { + if v == box { + ps.core.config.AllowedEncryptionPublicKeys = + append(ps.core.config.AllowedEncryptionPublicKeys[:k], + ps.core.config.AllowedEncryptionPublicKeys[k+1:]...) + } + } } // Gets the whitelist of allowed keys for incoming connections. -func (ps *peers) getAllowedEncryptionPublicKeys() []crypto.BoxPubKey { - ps.authMutex.RLock() - defer ps.authMutex.RUnlock() - keys := make([]crypto.BoxPubKey, 0, len(ps.allowedEncryptionPublicKeys)) - for key := range ps.allowedEncryptionPublicKeys { - keys = append(keys, key) - } - return keys +func (ps *peers) getAllowedEncryptionPublicKeys() []string { + ps.core.configMutex.RLock() + defer ps.core.configMutex.RUnlock() + return ps.core.config.AllowedEncryptionPublicKeys } // Atomically gets a map[switchPort]*peer of known peers. From 9d5085492ee045c658ee7d9eb3777b17828a6826 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 16 Jan 2019 20:38:51 +0000 Subject: [PATCH 2/3] Handle session firewall using central config --- src/yggdrasil/core.go | 9 ------ src/yggdrasil/session.go | 63 +++++++++------------------------------- 2 files changed, 14 insertions(+), 58 deletions(-) diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 3382562..ed07581 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -212,15 +212,6 @@ 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 diff --git a/src/yggdrasil/session.go b/src/yggdrasil/session.go index e29cd4f..4f632fa 100644 --- a/src/yggdrasil/session.go +++ b/src/yggdrasil/session.go @@ -7,7 +7,6 @@ package yggdrasil import ( "bytes" "encoding/hex" - "sync" "time" "github.com/yggdrasil-network/yggdrasil-go/src/address" @@ -115,14 +114,6 @@ type sessions struct { byTheirPerm map[crypto.BoxPubKey]*crypto.Handle addrToPerm map[address.Address]*crypto.BoxPubKey subnetToPerm map[address.Subnet]*crypto.BoxPubKey - // Options from the session firewall - sessionFirewallMutex sync.RWMutex - sessionFirewallEnabled bool - sessionFirewallAllowsDirect bool - sessionFirewallAllowsRemote bool - sessionFirewallAlwaysAllowsOutbound bool - sessionFirewallWhitelist []string - sessionFirewallBlacklist []string } // Initializes the session struct. @@ -155,51 +146,28 @@ func (ss *sessions) init(core *Core) { ss.lastCleanup = time.Now() } -// Enable or disable the session firewall -func (ss *sessions) setSessionFirewallState(enabled bool) { - ss.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - ss.sessionFirewallEnabled = enabled -} +// Determines whether the session firewall is enabled. +func (ss *sessions) isSessionFirewallEnabled() bool { + ss.core.configMutex.RLock() + defer ss.core.configMutex.RUnlock() -// 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.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - 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.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - ss.sessionFirewallWhitelist = whitelist -} - -// Set the session firewall blacklist - nodes never allowed to open sessions. -func (ss *sessions) setSessionFirewallBlacklist(blacklist []string) { - ss.sessionFirewallMutex.Lock() - defer ss.sessionFirewallMutex.Unlock() - ss.sessionFirewallBlacklist = blacklist + return ss.core.config.SessionFirewall.Enable } // Determines whether the session with a given publickey is allowed based on // session firewall rules. func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) bool { - ss.sessionFirewallMutex.RLock() - defer ss.sessionFirewallMutex.RUnlock() + ss.core.configMutex.RLock() + defer ss.core.configMutex.RUnlock() // Allow by default if the session firewall is disabled - if !ss.sessionFirewallEnabled { + if !ss.isSessionFirewallEnabled() { return true } // Prepare for checking whitelist/blacklist var box crypto.BoxPubKey // Reject blacklisted nodes - for _, b := range ss.sessionFirewallBlacklist { + for _, b := range ss.core.config.SessionFirewall.BlacklistEncryptionPublicKeys { key, err := hex.DecodeString(b) if err == nil { copy(box[:crypto.BoxPubKeyLen], key) @@ -209,7 +177,7 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b } } // Allow whitelisted nodes - for _, b := range ss.sessionFirewallWhitelist { + for _, b := range ss.core.config.SessionFirewall.WhitelistEncryptionPublicKeys { key, err := hex.DecodeString(b) if err == nil { copy(box[:crypto.BoxPubKeyLen], key) @@ -219,7 +187,7 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b } } // Allow outbound sessions if appropriate - if ss.sessionFirewallAlwaysAllowsOutbound { + if ss.core.config.SessionFirewall.AlwaysAllowOutbound { if initiator { return true } @@ -233,11 +201,11 @@ func (ss *sessions) isSessionAllowed(pubkey *crypto.BoxPubKey, initiator bool) b } } // Allow direct peers if appropriate - if ss.sessionFirewallAllowsDirect && isDirectPeer { + if ss.core.config.SessionFirewall.AllowFromDirect && isDirectPeer { return true } // Allow remote nodes if appropriate - if ss.sessionFirewallAllowsRemote && !isDirectPeer { + if ss.core.config.SessionFirewall.AllowFromRemote && !isDirectPeer { return true } // Finally, default-deny if not matching any of the above rules @@ -474,14 +442,11 @@ 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 - ss.sessionFirewallMutex.RLock() - if !isIn && ss.sessionFirewallEnabled { + if !isIn && ss.isSessionFirewallEnabled() { if !ss.isSessionAllowed(&ping.SendPermPub, false) { - ss.sessionFirewallMutex.RUnlock() return } } - ss.sessionFirewallMutex.RUnlock() if !isIn || sinfo.timedout() { if isIn { sinfo.close() From c839012580cdf84a46121dc453a73811dd86d688 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 17 Jan 2019 23:06:59 +0000 Subject: [PATCH 3/3] Fix source address selection --- src/yggdrasil/tcp.go | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index ca9ea09..78e39ef 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -245,15 +245,27 @@ func (iface *tcpInterface) call(saddr string, socksaddr *string, sintf string) { if err != nil { continue } - if (src.To4() != nil) == (dst.IP.To4() != nil) { - if addrindex == len(addrs)-1 || src.IsGlobalUnicast() { - dialer.LocalAddr = &net.TCPAddr{ - IP: src, - Port: 0, - Zone: sintf, - } - break + if src.Equal(dst.IP) { + continue + } + if !src.IsGlobalUnicast() && !src.IsLinkLocalUnicast() { + continue + } + bothglobal := src.IsGlobalUnicast() == dst.IP.IsGlobalUnicast() + bothlinklocal := src.IsLinkLocalUnicast() == dst.IP.IsLinkLocalUnicast() + if !bothglobal && !bothlinklocal { + continue + } + if (src.To4() != nil) != (dst.IP.To4() != nil) { + continue + } + if bothglobal || bothlinklocal || addrindex == len(addrs)-1 { + dialer.LocalAddr = &net.TCPAddr{ + IP: src, + Port: 0, + Zone: sintf, } + break } } if dialer.LocalAddr == nil {