Merge pull request #1124 from weaveworks/issues/1037-mitigate-entropy-depletion

Mitigate entropy depletion

Closes #1037.
This commit is contained in:
Matthias Radestock committed 2015-07-13 19:06:54 +01:00
commit 1f3d054b09
3 files changed
+85 -7

No files matched your search

+9 -6
View File
@@ -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()
}
}()
}
+42
View File
@@ -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
View File
@@ -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