mirror of
https://github.com/slackhq/nebula.git
synced 2026-05-16 04:47:38 +02:00
tweaks
This commit is contained in:
@@ -329,8 +329,7 @@ func (f *Interface) listenOut(i int) {
|
|||||||
nb := make([]byte, 12, 12)
|
nb := make([]byte, 12, 12)
|
||||||
|
|
||||||
listener := func(fromUdpAddr netip.AddrPort, payload []byte, meta udp.RxMeta) {
|
listener := func(fromUdpAddr netip.AddrPort, payload []byte, meta udp.RxMeta) {
|
||||||
plaintext := f.batchers[i].Reserve(len(payload))
|
f.readOutsidePackets(ViaSender{UdpAddr: fromUdpAddr}, payload, h, fwPacket, lhh, nb, i, ctCache.Get())
|
||||||
f.readOutsidePackets(ViaSender{UdpAddr: fromUdpAddr}, plaintext[:0], payload, h, fwPacket, lhh, nb, i, ctCache.Get())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flusher := func() {
|
flusher := func() {
|
||||||
|
|||||||
@@ -147,6 +147,62 @@ func buildCipherStatesB(b *testing.B, c noise.CipherFunc) (*noise.CipherState, *
|
|||||||
return eI, dR
|
return eI, dR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDecryptDangerRelayShapeNoAlloc covers the AD-only relay path used in
|
||||||
|
// outside.go's handleOutsideRelayPacket: the body is AD, the trailing 16 bytes
|
||||||
|
// are the AEAD tag, the plaintext is empty, and the caller passes nil as the
|
||||||
|
// destination because it only needs the auth side-effect. The call must
|
||||||
|
// succeed, return an empty plaintext, and not allocate on the hot path.
|
||||||
|
func TestDecryptDangerRelayShapeNoAlloc(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
c noise.CipherFunc
|
||||||
|
wrap func(*noise.CipherState) CipherState
|
||||||
|
}{
|
||||||
|
{"AESGCM", CipherAESGCM, func(cs *noise.CipherState) CipherState { return NewCipherStateAESGCM(cs) }},
|
||||||
|
{"ChaChaPoly", noise.CipherChaChaPoly, func(cs *noise.CipherState) CipherState { return NewCipherStateChaChaPoly(cs) }},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
encCS, decCS := buildCipherStates(t, tc.c)
|
||||||
|
enc, dec := tc.wrap(encCS), tc.wrap(decCS)
|
||||||
|
|
||||||
|
ad := make([]byte, 1200) // typical relay packet body size
|
||||||
|
for i := range ad {
|
||||||
|
ad[i] = byte(i)
|
||||||
|
}
|
||||||
|
nb := make([]byte, 12)
|
||||||
|
|
||||||
|
// Build the "signature value" the way handleOutsideRelayPacket sees it:
|
||||||
|
// empty plaintext encrypted with the body as AD yields just the 16-byte tag.
|
||||||
|
tag, err := enc.EncryptDanger(nil, ad, nil, 1, nb)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, tag, dec.Overhead())
|
||||||
|
|
||||||
|
// Sanity: the relay-shaped call returns empty plaintext, no error.
|
||||||
|
out, err := dec.DecryptDanger(nil, ad, tag, 1, nb)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Empty(t, out)
|
||||||
|
|
||||||
|
// Tampering with the AD must fail authentication.
|
||||||
|
ad[0] ^= 0xff
|
||||||
|
_, err = dec.DecryptDanger(nil, ad, tag, 1, nb)
|
||||||
|
require.Error(t, err)
|
||||||
|
ad[0] ^= 0xff
|
||||||
|
|
||||||
|
// The hot path must not allocate. AllocsPerRun does a warm-up run, so any
|
||||||
|
// one-time setup is excluded. Counter has to advance so the AEAD nonce is
|
||||||
|
// unique per call, but we don't care whether the auth succeeds — we only
|
||||||
|
// care about whether the call path allocates.
|
||||||
|
var counter uint64 = 2
|
||||||
|
allocs := testing.AllocsPerRun(100, func() {
|
||||||
|
_, _ = dec.DecryptDanger(nil, ad, tag, counter, nb)
|
||||||
|
counter++
|
||||||
|
})
|
||||||
|
assert.Equal(t, 0.0, allocs, "DecryptDanger(nil, ...) must not allocate")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCipherStateNilSafety(t *testing.T) {
|
func TestCipherStateNilSafety(t *testing.T) {
|
||||||
var aes *CipherStateAESGCM
|
var aes *CipherStateAESGCM
|
||||||
_, err := aes.EncryptDanger(nil, nil, nil, 0, make([]byte, 12))
|
_, err := aes.EncryptDanger(nil, nil, nil, 0, make([]byte, 12))
|
||||||
|
|||||||
17
outside.go
17
outside.go
@@ -22,7 +22,7 @@ const (
|
|||||||
|
|
||||||
var ErrOutOfWindow = errors.New("out of window packet")
|
var ErrOutOfWindow = errors.New("out of window packet")
|
||||||
|
|
||||||
func (f *Interface) readOutsidePackets(via ViaSender, out []byte, packet []byte, h *header.H, fwPacket *firewall.Packet, lhf *LightHouseHandler, nb []byte, q int, localCache firewall.ConntrackCache) {
|
func (f *Interface) readOutsidePackets(via ViaSender, packet []byte, h *header.H, fwPacket *firewall.Packet, lhf *LightHouseHandler, nb []byte, q int, localCache firewall.ConntrackCache) {
|
||||||
err := h.Parse(packet)
|
err := h.Parse(packet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Hole punch packets are 0 or 1 byte big, so lets ignore printing those errors
|
// Hole punch packets are 0 or 1 byte big, so lets ignore printing those errors
|
||||||
@@ -110,10 +110,11 @@ func (f *Interface) readOutsidePackets(via ViaSender, out []byte, packet []byte,
|
|||||||
|
|
||||||
// Relay packets are special
|
// Relay packets are special
|
||||||
if isMessageRelay {
|
if isMessageRelay {
|
||||||
f.handleOutsideRelayPacket(hostinfo, via, out, packet, h, fwPacket, lhf, nb, q, localCache)
|
f.handleOutsideRelayPacket(hostinfo, via, packet, h, fwPacket, lhf, nb, q, localCache)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out := f.batchers[q].Reserve(len(packet))
|
||||||
out, err = f.decrypt(hostinfo, h.MessageCounter, out, packet, h, nb)
|
out, err = f.decrypt(hostinfo, h.MessageCounter, out, packet, h, nb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if f.l.Enabled(context.Background(), slog.LevelDebug) {
|
if f.l.Enabled(context.Background(), slog.LevelDebug) {
|
||||||
@@ -167,7 +168,7 @@ func (f *Interface) readOutsidePackets(via ViaSender, out []byte, packet []byte,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Interface) handleOutsideRelayPacket(hostinfo *HostInfo, via ViaSender, out []byte, packet []byte, h *header.H, fwPacket *firewall.Packet, lhf *LightHouseHandler, nb []byte, q int, localCache firewall.ConntrackCache) {
|
func (f *Interface) handleOutsideRelayPacket(hostinfo *HostInfo, via ViaSender, packet []byte, h *header.H, fwPacket *firewall.Packet, lhf *LightHouseHandler, nb []byte, q int, localCache firewall.ConntrackCache) {
|
||||||
// The entire body is sent as AD, not encrypted.
|
// The entire body is sent as AD, not encrypted.
|
||||||
// The packet consists of a 16-byte parsed Nebula header, Associated Data-protected payload, and a trailing 16-byte AEAD signature value.
|
// The packet consists of a 16-byte parsed Nebula header, Associated Data-protected payload, and a trailing 16-byte AEAD signature value.
|
||||||
// The packet is guaranteed to be at least 16 bytes at this point, b/c it got past the h.Parse() call above. If it's
|
// The packet is guaranteed to be at least 16 bytes at this point, b/c it got past the h.Parse() call above. If it's
|
||||||
@@ -175,9 +176,10 @@ func (f *Interface) handleOutsideRelayPacket(hostinfo *HostInfo, via ViaSender,
|
|||||||
// which will gracefully fail in the DecryptDanger call.
|
// which will gracefully fail in the DecryptDanger call.
|
||||||
signedPayload := packet[:len(packet)-hostinfo.ConnectionState.dKey.Overhead()]
|
signedPayload := packet[:len(packet)-hostinfo.ConnectionState.dKey.Overhead()]
|
||||||
signatureValue := packet[len(packet)-hostinfo.ConnectionState.dKey.Overhead():]
|
signatureValue := packet[len(packet)-hostinfo.ConnectionState.dKey.Overhead():]
|
||||||
var err error
|
// The decrypted output is empty (relay packets carry their payload as AD) and unused.
|
||||||
out, err = hostinfo.ConnectionState.dKey.DecryptDanger(out, signedPayload, signatureValue, h.MessageCounter, nb)
|
// The recursive readOutsidePackets call below operates on signedPayload. Passing
|
||||||
if err != nil {
|
// nil avoids reserving an arena slot.
|
||||||
|
if _, err := hostinfo.ConnectionState.dKey.DecryptDanger(nil, signedPayload, signatureValue, h.MessageCounter, nb); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Successfully validated the thing. Get rid of the Relay header.
|
// Successfully validated the thing. Get rid of the Relay header.
|
||||||
@@ -210,7 +212,7 @@ func (f *Interface) handleOutsideRelayPacket(hostinfo *HostInfo, via ViaSender,
|
|||||||
relay: relay,
|
relay: relay,
|
||||||
IsRelayed: true,
|
IsRelayed: true,
|
||||||
}
|
}
|
||||||
f.readOutsidePackets(via, out[:0], signedPayload, h, fwPacket, lhf, nb, q, localCache)
|
f.readOutsidePackets(via, signedPayload, h, fwPacket, lhf, nb, q, localCache)
|
||||||
case ForwardingType:
|
case ForwardingType:
|
||||||
// Find the target HostInfo relay object
|
// Find the target HostInfo relay object
|
||||||
targetHI, targetRelay, err := f.hostMap.QueryVpnAddrsRelayFor(hostinfo.vpnAddrs, relay.PeerAddr)
|
targetHI, targetRelay, err := f.hostMap.QueryVpnAddrsRelayFor(hostinfo.vpnAddrs, relay.PeerAddr)
|
||||||
@@ -229,6 +231,7 @@ func (f *Interface) handleOutsideRelayPacket(hostinfo *HostInfo, via ViaSender,
|
|||||||
case ForwardingType:
|
case ForwardingType:
|
||||||
// Forward this packet through the relay tunnel
|
// Forward this packet through the relay tunnel
|
||||||
// Find the target HostInfo //todo it would potentially be nice to batch these
|
// Find the target HostInfo //todo it would potentially be nice to batch these
|
||||||
|
out := f.batchers[q].Reserve(len(packet) + header.Len + hostinfo.ConnectionState.dKey.Overhead())[:0]
|
||||||
f.SendVia(targetHI, targetRelay, signedPayload, nb, out, false)
|
f.SendVia(targetHI, targetRelay, signedPayload, nb, out, false)
|
||||||
case TerminalType:
|
case TerminalType:
|
||||||
hostinfo.logger(f.l).Error("Unexpected Relay Type of Terminal")
|
hostinfo.logger(f.l).Error("Unexpected Relay Type of Terminal")
|
||||||
|
|||||||
Reference in New Issue
Block a user