Merge pull request #1124 from weaveworks/issues/1037-mitigate-entropy-depletion
Mitigate entropy depletion Closes #1037.
This commit is contained in:
commit
1f3d054b09
3 files changed
+85
-7
No files matched your search
+9
-6
@@ -16,16 +16,17 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
macMaxAge = 10 * time.Minute // [1]
|
||||
tcpAcceptDelay = 1 * time.Millisecond // [2]
|
||||
macMaxAge = 10 * time.Minute // [1]
|
||||
acceptMaxTokens = 100 // [2]
|
||||
acceptTokenDelay = 100 * time.Millisecond // [3]
|
||||
)
|
||||
|
||||
// [1] should be greater than typical ARP cache expiries, i.e. > 3/2 *
|
||||
// /proc/sys/net/ipv4_neigh/*/base_reachable_time_ms on Linux
|
||||
|
||||
// [2] time to wait between accepting tcp connections. This guards
|
||||
// against brute-force attacks on the password when encryption is in
|
||||
// use. It is also a basic DoS defence.
|
||||
// [2] capacity of token bucket for rate limiting accepts
|
||||
|
||||
// [3] control rate at which new tokens are added to the bucket
|
||||
|
||||
type LogFrameFunc func(string, []byte, *layers.Ethernet)
|
||||
|
||||
@@ -50,6 +51,7 @@ type Router struct {
|
||||
gossipChannels GossipChannels
|
||||
TopologyGossip Gossip
|
||||
UDPListener *net.UDPConn
|
||||
acceptLimiter *TokenBucket
|
||||
}
|
||||
|
||||
type PacketSource interface {
|
||||
@@ -81,6 +83,7 @@ func NewRouter(config Config, name PeerName, nickName string) *Router {
|
||||
router.Routes = NewRoutes(router.Ourself, router.Peers)
|
||||
router.ConnectionMaker = NewConnectionMaker(router.Ourself, router.Peers, router.Port, router.PeerDiscovery)
|
||||
router.TopologyGossip = router.NewGossip("topology", router)
|
||||
router.acceptLimiter = NewTokenBucket(acceptMaxTokens, acceptTokenDelay)
|
||||
return router
|
||||
}
|
||||
|
||||
@@ -212,7 +215,7 @@ func (router *Router) listenTCP(localPort int) {
|
||||
continue
|
||||
}
|
||||
router.acceptTCP(tcpConn)
|
||||
time.Sleep(tcpAcceptDelay)
|
||||
router.acceptLimiter.Wait()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type TokenBucket struct {
|
||||
capacity int64 // Maximum capacity of bucket
|
||||
tokenInterval time.Duration // Token replenishment rate
|
||||
refillDuration time.Duration // Time to refill from empty
|
||||
earliestUnspentToken time.Time
|
||||
}
|
||||
|
||||
func NewTokenBucket(capacity int64, tokenInterval time.Duration) *TokenBucket {
|
||||
tb := TokenBucket{
|
||||
capacity: capacity,
|
||||
tokenInterval: tokenInterval,
|
||||
refillDuration: tokenInterval * time.Duration(capacity)}
|
||||
|
||||
tb.earliestUnspentToken = tb.capacityToken()
|
||||
|
||||
return &tb
|
||||
}
|
||||
|
||||
func (tb *TokenBucket) Wait() {
|
||||
// If earliest unspent token is in the future, sleep until then
|
||||
time.Sleep(tb.earliestUnspentToken.Sub(time.Now()))
|
||||
|
||||
// Alternatively, enforce bucket capacity if necessary
|
||||
capacityToken := tb.capacityToken()
|
||||
if tb.earliestUnspentToken.Before(capacityToken) {
|
||||
tb.earliestUnspentToken = capacityToken
|
||||
}
|
||||
|
||||
// 'Remove' a token from the bucket
|
||||
tb.earliestUnspentToken = tb.earliestUnspentToken.Add(tb.tokenInterval)
|
||||
}
|
||||
|
||||
// Determine the historic token timestamp representing a full bucket
|
||||
func (tb *TokenBucket) capacityToken() time.Time {
|
||||
return time.Now().Add(-tb.refillDuration).Truncate(tb.tokenInterval)
|
||||
}
|
||||
+34
-1
@@ -278,7 +278,7 @@ password, and the result is hashed through SHA256, to form the final
|
||||
ephemeral session key. Thus the supplied password is never exchanged
|
||||
directly, and is thoroughly mixed into the shared secret. Furthermore,
|
||||
the rate at which TCP connections are accepted is limited by weave to
|
||||
1kHz, which twarts online dictionary attacks on reasonably strong
|
||||
10Hz, which twarts online dictionary attacks on reasonably strong
|
||||
passwords.
|
||||
|
||||
The shared key formed by Diffie-Hellman is 256 bits long, appending
|
||||
@@ -293,6 +293,39 @@ not be able to form valid ephemeral session keys.
|
||||
The same ephemeral session key is used for both TCP and UDP traffic
|
||||
between two peers.
|
||||
|
||||
<a name="csprng"></a> Generating fresh keys for every connection
|
||||
provides forward secrecy at the cost of placing a demand on the Linux
|
||||
CSPRNG (accessed by `GenerateKey` via `/dev/urandom`) proportional to
|
||||
the number of inbound connection attempts. Weave has accept throttling
|
||||
to mitigate against denial of service attacks that seek to deplete the
|
||||
CSPRNG entropy pool, however even at the lower bound of ten requests
|
||||
per second there may not be enough entropy gathered on a headless
|
||||
system to keep pace.
|
||||
|
||||
Under such conditions, the consequences will be limited to slowing
|
||||
down processes reading from the blocking `/dev/random` device as the
|
||||
kernel waits for enough new entropy to be harvested. It is important
|
||||
to note that contrary to intuition this low entropy state does not
|
||||
compromise the ongoing use of `/dev/urandom` - [expert
|
||||
opinion](http://blog.cr.yp.to/20140205-entropy.html)
|
||||
asserts that as long as the CSPRNG is seeded with enough entropy (e.g.
|
||||
256 bits) before random number generation commences then the output is
|
||||
entirely safe for use as key material.
|
||||
|
||||
By way of comparison, this is exactly how OpenSSL works - it reads 256
|
||||
bits of entropy at startup, and uses that to seed an internal CSPRNG
|
||||
which is used thenceforth to generate keys. Whilst we could have taken
|
||||
the same approach and built our own CSPRNG to work around the
|
||||
potential `/dev/random` blocking issue, we thought it was much more
|
||||
prudent to rely on the [heavily
|
||||
scrutinised](http://eprint.iacr.org/2012/251.pdf) Linux random number
|
||||
generator as [advised
|
||||
here](http://cr.yp.to/highspeed/coolnacl-20120725.pdf) (page 10,
|
||||
'Centralizing randomness'). The aforementioned notwithstanding, if
|
||||
weave's demand on `/dev/urandom` is causing you problems with blocking
|
||||
`/dev/random` reads, please get in touch with us - we'd love to hear
|
||||
about your use case.
|
||||
|
||||
#### TCP
|
||||
|
||||
TCP connection are only used to exchange topology information between
|
||||
|
||||
Reference in new issue
Block a user