5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-22 20:00:27 +00:00

Merge pull request #74 from Arceliar/peerauth

Partial support for authenticated peers
This commit is contained in:
Neil Alexander 2018-05-07 23:29:36 +01:00 committed by GitHub
commit fcf7fe71af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 165 additions and 48 deletions

View File

@ -3,6 +3,7 @@ package yggdrasil
import "net" import "net"
import "os" import "os"
import "bytes" import "bytes"
import "encoding/hex"
import "errors" import "errors"
import "fmt" import "fmt"
import "net/url" import "net/url"
@ -104,6 +105,23 @@ func (a *admin) init(c *Core, listenaddr string) {
*out = []byte(a.printInfos([]admin_nodeInfo{info})) *out = []byte(a.printInfos([]admin_nodeInfo{info}))
} }
}) })
a.addHandler("getAllowedBoxPubs", nil, func(out *[]byte, _ ...string) {
*out = []byte(a.getAllowedBoxPubs())
})
a.addHandler("addAllowedBoxPub", []string{"<boxPubKey>"}, func(out *[]byte, saddr ...string) {
if a.addAllowedBoxPub(saddr[0]) == nil {
*out = []byte("Adding key: " + saddr[0] + "\n")
} else {
*out = []byte("Failed to add key: " + saddr[0] + "\n")
}
})
a.addHandler("removeAllowedBoxPub", []string{"<boxPubKey>"}, func(out *[]byte, sport ...string) {
if a.removeAllowedBoxPub(sport[0]) == nil {
*out = []byte("Removing key: " + sport[0] + "\n")
} else {
*out = []byte("Failed to remove key: " + sport[0] + "\n")
}
})
go a.listen() go a.listen()
} }
@ -347,6 +365,36 @@ func (a *admin) getData_getSessions() []admin_nodeInfo {
return infos return infos
} }
func (a *admin) getAllowedBoxPubs() string {
pubs := a.core.peers.getAllowedBoxPubs()
var out []string
for _, pub := range pubs {
out = append(out, hex.EncodeToString(pub[:]))
}
out = append(out, "")
return strings.Join(out, "\n")
}
func (a *admin) addAllowedBoxPub(bstr string) (err error) {
boxBytes, err := hex.DecodeString(bstr)
if err == nil {
var box boxPubKey
copy(box[:], boxBytes)
a.core.peers.addAllowedBoxPub(&box)
}
return
}
func (a *admin) removeAllowedBoxPub(bstr string) (err error) {
boxBytes, err := hex.DecodeString(bstr)
if err == nil {
var box boxPubKey
copy(box[:], boxBytes)
a.core.peers.removeAllowedBoxPub(&box)
}
return
}
func (a *admin) getResponse_dot() []byte { func (a *admin) getResponse_dot() []byte {
self := a.getData_getSelf().asMap() self := a.getData_getSelf().asMap()
myAddr := self["IP"] myAddr := self["IP"]

View File

@ -2,19 +2,20 @@ package config
// NodeConfig defines all configuration values needed to run a signle yggdrasil node // NodeConfig defines all configuration values needed to run a signle yggdrasil node
type NodeConfig struct { type NodeConfig struct {
Listen string Listen string
AdminListen string AdminListen string
Peers []string Peers []string
BoxPub string AllowedBoxPubs []string
BoxPriv string BoxPub string
SigPub string BoxPriv string
SigPriv string SigPub string
Multicast bool SigPriv string
LinkLocal string Multicast bool
IfName string LinkLocal string
IfTAPMode bool IfName string
IfMTU int IfTAPMode bool
Net NetConfig IfMTU int
Net NetConfig
} }
// NetConfig defines network/proxy related configuration values // NetConfig defines network/proxy related configuration values

View File

@ -44,6 +44,7 @@ func (c *Core) init(bpub *boxPubKey,
c.log = log.New(ioutil.Discard, "", 0) c.log = log.New(ioutil.Discard, "", 0)
c.boxPub, c.boxPriv = *bpub, *bpriv c.boxPub, c.boxPriv = *bpub, *bpriv
c.sigPub, c.sigPriv = *spub, *spriv c.sigPub, c.sigPriv = *spub, *spriv
c.admin.core = c
c.sigs.init() c.sigs.init()
c.searches.init(c) c.searches.init(c)
c.dht.init(c) c.dht.init(c)

View File

@ -397,6 +397,13 @@ func (c *Core) DEBUG_setIfceExpr(expr *regexp.Regexp) {
c.ifceExpr = expr c.ifceExpr = expr
} }
func (c *Core) DEBUG_addAllowedBoxPub(boxStr string) {
err := c.admin.addAllowedBoxPub(boxStr)
if err != nil {
panic(err)
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
func DEBUG_simLinkPeers(p, q *peer) { func DEBUG_simLinkPeers(p, q *peer) {

View File

@ -34,6 +34,8 @@ type peers struct {
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[Port]*peer, use CoW semantics
//ports map[Port]*peer //ports map[Port]*peer
authMutex sync.RWMutex
allowedBoxPubs map[boxPubKey]struct{}
} }
func (ps *peers) init(c *Core) { func (ps *peers) init(c *Core) {
@ -41,6 +43,36 @@ func (ps *peers) init(c *Core) {
defer ps.mutex.Unlock() defer ps.mutex.Unlock()
ps.putPorts(make(map[switchPort]*peer)) ps.putPorts(make(map[switchPort]*peer))
ps.core = c ps.core = c
ps.allowedBoxPubs = make(map[boxPubKey]struct{})
}
func (ps *peers) isAllowedBoxPub(box *boxPubKey) bool {
ps.authMutex.RLock()
defer ps.authMutex.RUnlock()
_, isIn := ps.allowedBoxPubs[*box]
return isIn || len(ps.allowedBoxPubs) == 0
}
func (ps *peers) addAllowedBoxPub(box *boxPubKey) {
ps.authMutex.Lock()
defer ps.authMutex.Unlock()
ps.allowedBoxPubs[*box] = struct{}{}
}
func (ps *peers) removeAllowedBoxPub(box *boxPubKey) {
ps.authMutex.Lock()
defer ps.authMutex.Unlock()
delete(ps.allowedBoxPubs, *box)
}
func (ps *peers) getAllowedBoxPubs() []boxPubKey {
ps.authMutex.RLock()
defer ps.authMutex.RUnlock()
keys := make([]boxPubKey, 0, len(ps.allowedBoxPubs))
for key := range ps.allowedBoxPubs {
keys = append(keys, key)
}
return keys
} }
func (ps *peers) getPorts() map[switchPort]*peer { func (ps *peers) getPorts() map[switchPort]*peer {
@ -75,6 +107,8 @@ type peer struct {
throttle uint8 throttle uint8
// Called when a peer is removed, to close the underlying connection, or via admin api // Called when a peer is removed, to close the underlying connection, or via admin api
close func() close func()
// To allow the peer to call close if idle for too long
lastAnc time.Time
} }
const peer_Throttle = 1 const peer_Throttle = 1
@ -99,14 +133,11 @@ func (p *peer) updateBandwidth(bytes int, duration time.Duration) {
func (ps *peers) newPeer(box *boxPubKey, func (ps *peers) newPeer(box *boxPubKey,
sig *sigPubKey) *peer { sig *sigPubKey) *peer {
//in <-chan []byte,
//out chan<- []byte) *peer {
p := peer{box: *box, p := peer{box: *box,
sig: *sig, sig: *sig,
shared: *getSharedKey(&ps.core.boxPriv, box), shared: *getSharedKey(&ps.core.boxPriv, box),
//in: in, lastAnc: time.Now(),
//out: out, core: ps.core}
core: ps.core}
ps.mutex.Lock() ps.mutex.Lock()
defer ps.mutex.Unlock() defer ps.mutex.Unlock()
oldPorts := ps.getPorts() oldPorts := ps.getPorts()
@ -158,31 +189,33 @@ func (p *peer) linkLoop(in <-chan []byte) {
} }
p.handleLinkTraffic(packet) p.handleLinkTraffic(packet)
case <-ticker.C: case <-ticker.C:
{ if time.Since(p.lastAnc) > 16*time.Second && p.close != nil {
p.throttle = 0 // Seems to have timed out, try to trigger a close
if p.port == 0 { p.close()
continue
} // Don't send announces on selfInterface
p.myMsg, p.mySigs = p.core.switchTable.createMessage(p.port)
var update bool
switch {
case p.msgAnc == nil:
update = true
case lastRSeq != p.msgAnc.seq:
update = true
case p.msgAnc.rseq != p.myMsg.seq:
update = true
case counter%4 == 0:
update = true
}
if update {
if p.msgAnc != nil {
lastRSeq = p.msgAnc.seq
}
p.sendSwitchAnnounce()
}
counter = (counter + 1) % 4
} }
p.throttle = 0
if p.port == 0 {
continue
} // Don't send announces on selfInterface
p.myMsg, p.mySigs = p.core.switchTable.createMessage(p.port)
var update bool
switch {
case p.msgAnc == nil:
update = true
case lastRSeq != p.msgAnc.seq:
update = true
case p.msgAnc.rseq != p.myMsg.seq:
update = true
case counter%4 == 0:
update = true
}
if update {
if p.msgAnc != nil {
lastRSeq = p.msgAnc.seq
}
p.sendSwitchAnnounce()
}
counter = (counter + 1) % 4
} }
} }
} }
@ -210,6 +243,10 @@ func (p *peer) handlePacket(packet []byte, linkIn chan<- []byte) {
} }
func (p *peer) handleTraffic(packet []byte, pTypeLen int) { func (p *peer) handleTraffic(packet []byte, pTypeLen int) {
if p.port != 0 && p.msgAnc == nil {
// Drop traffic until the peer manages to send us at least one anc
return
}
ttl, ttlLen := wire_decode_uint64(packet[pTypeLen:]) ttl, ttlLen := wire_decode_uint64(packet[pTypeLen:])
ttlBegin := pTypeLen ttlBegin := pTypeLen
ttlEnd := pTypeLen + ttlLen ttlEnd := pTypeLen + ttlLen
@ -292,6 +329,7 @@ func (p *peer) handleSwitchAnnounce(packet []byte) {
} }
p.msgAnc = &anc p.msgAnc = &anc
p.processSwitchMessage() p.processSwitchMessage()
p.lastAnc = time.Now()
} }
func (p *peer) requestHop(hop uint64) { func (p *peer) requestHop(hop uint64) {

View File

@ -62,7 +62,7 @@ func (iface *tcpInterface) listener() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
go iface.handler(sock) go iface.handler(sock, true)
} }
} }
@ -81,7 +81,7 @@ func (iface *tcpInterface) callWithConn(conn net.Conn) {
delete(iface.calls, raddr) delete(iface.calls, raddr)
iface.mutex.Unlock() iface.mutex.Unlock()
}() }()
iface.handler(conn) iface.handler(conn, false)
} }
}() }()
} }
@ -106,12 +106,12 @@ func (iface *tcpInterface) call(saddr string) {
if err != nil { if err != nil {
return return
} }
iface.handler(conn) iface.handler(conn, false)
} }
}() }()
} }
func (iface *tcpInterface) handler(sock net.Conn) { func (iface *tcpInterface) handler(sock net.Conn, incoming bool) {
defer sock.Close() defer sock.Close()
// Get our keys // Get our keys
keys := []byte{} keys := []byte{}
@ -150,6 +150,15 @@ func (iface *tcpInterface) handler(sock net.Conn) {
if equiv(info.sig[:], iface.core.sigPub[:]) { if equiv(info.sig[:], iface.core.sigPub[:]) {
return return
} }
// Check if we're authorized to connect to this key / IP
if incoming && !iface.core.peers.isAllowedBoxPub(&info.box) {
// Allow unauthorized peers if they're link-local
raddrStr, _, _ := net.SplitHostPort(sock.RemoteAddr().String())
raddr := net.ParseIP(raddrStr)
if !raddr.IsLinkLocalUnicast() {
return
}
}
// Check if we already have a connection to this node, close and block if yes // Check if we already have a connection to this node, close and block if yes
info.localAddr, _, _ = net.SplitHostPort(sock.LocalAddr().String()) info.localAddr, _, _ = net.SplitHostPort(sock.LocalAddr().String())
info.remoteAddr, _, _ = net.SplitHostPort(sock.RemoteAddr().String()) info.remoteAddr, _, _ = net.SplitHostPort(sock.RemoteAddr().String())

View File

@ -204,6 +204,14 @@ func (iface *udpInterface) handleKeys(msg []byte, addr connAddr) {
iface.mutex.RUnlock() iface.mutex.RUnlock()
if !isIn { if !isIn {
udpAddr := addr.toUDPAddr() udpAddr := addr.toUDPAddr()
// Check if we're authorized to connect to this key / IP
// TODO monitor and always allow outgoing connections
if !iface.core.peers.isAllowedBoxPub(&ks.box) {
// Allow unauthorized peers if they're link-local
if !udpAddr.IP.IsLinkLocalUnicast() {
return
}
}
themNodeID := getNodeID(&ks.box) themNodeID := getNodeID(&ks.box)
themAddr := address_addrForNodeID(themNodeID) themAddr := address_addrForNodeID(themNodeID)
themAddrString := net.IP(themAddr[:]).String() themAddrString := net.IP(themAddr[:]).String()

View File

@ -66,6 +66,10 @@ func (n *node) init(cfg *nodeConfig, logger *log.Logger) {
logger.Println("Starting admin socket...") logger.Println("Starting admin socket...")
n.core.DEBUG_setupAndStartAdminInterface(cfg.AdminListen) n.core.DEBUG_setupAndStartAdminInterface(cfg.AdminListen)
logger.Println("Started admin socket") logger.Println("Started admin socket")
for _, pBoxStr := range cfg.AllowedBoxPubs {
n.core.DEBUG_addAllowedBoxPub(pBoxStr)
}
go func() { go func() {
if len(cfg.Peers) == 0 { if len(cfg.Peers) == 0 {
return return
@ -97,6 +101,7 @@ func generateConfig(isAutoconf bool) *nodeConfig {
cfg.SigPub = hex.EncodeToString(spub[:]) cfg.SigPub = hex.EncodeToString(spub[:])
cfg.SigPriv = hex.EncodeToString(spriv[:]) cfg.SigPriv = hex.EncodeToString(spriv[:])
cfg.Peers = []string{} cfg.Peers = []string{}
cfg.AllowedBoxPubs = []string{}
cfg.Multicast = true cfg.Multicast = true
cfg.LinkLocal = "" cfg.LinkLocal = ""
cfg.IfName = core.DEBUG_GetTUNDefaultIfName() cfg.IfName = core.DEBUG_GetTUNDefaultIfName()