mirror of https://github.com/slackhq/nebula.git
Add an additional transitional mode to get us to enforced safely
This commit is contained in:
parent
c1ed78ffc7
commit
ba8646fa83
|
@ -190,37 +190,69 @@ func TestPSK(t *testing.T) {
|
|||
myPskMode nebula.PskMode
|
||||
theirPskMode nebula.PskMode
|
||||
}{
|
||||
// None and transitional-accepting both ways
|
||||
{
|
||||
name: "none to transitional",
|
||||
name: "none to transitional-accepting",
|
||||
myPskMode: nebula.PskNone,
|
||||
theirPskMode: nebula.PskTransitional,
|
||||
theirPskMode: nebula.PskTransitionalAccepting,
|
||||
},
|
||||
{
|
||||
name: "transitional to none",
|
||||
myPskMode: nebula.PskTransitional,
|
||||
name: "transitional-accepting to none",
|
||||
myPskMode: nebula.PskTransitionalAccepting,
|
||||
theirPskMode: nebula.PskNone,
|
||||
},
|
||||
|
||||
// All transitional-accepting
|
||||
{
|
||||
name: "both transitional",
|
||||
myPskMode: nebula.PskTransitional,
|
||||
theirPskMode: nebula.PskTransitional,
|
||||
name: "both transitional-accepting",
|
||||
myPskMode: nebula.PskTransitionalAccepting,
|
||||
theirPskMode: nebula.PskTransitionalAccepting,
|
||||
},
|
||||
|
||||
// transitional-accepting and transitional-sending both ways
|
||||
{
|
||||
name: "enforced to transitional",
|
||||
myPskMode: nebula.PskEnforced,
|
||||
theirPskMode: nebula.PskTransitional,
|
||||
name: "transitional-accepting to transitional-sending",
|
||||
myPskMode: nebula.PskTransitionalAccepting,
|
||||
theirPskMode: nebula.PskTransitionalSending,
|
||||
},
|
||||
{
|
||||
name: "transitional to enforced",
|
||||
myPskMode: nebula.PskTransitional,
|
||||
name: "transitional-sending to transitional-accepting",
|
||||
myPskMode: nebula.PskTransitionalSending,
|
||||
theirPskMode: nebula.PskTransitionalAccepting,
|
||||
},
|
||||
|
||||
// All transitional-sending
|
||||
{
|
||||
name: "transitional-sending to transitional-sending",
|
||||
myPskMode: nebula.PskTransitionalSending,
|
||||
theirPskMode: nebula.PskTransitionalSending,
|
||||
},
|
||||
|
||||
// enforced and transitional-sending both ways
|
||||
{
|
||||
name: "enforced to transitional-sending",
|
||||
myPskMode: nebula.PskEnforced,
|
||||
theirPskMode: nebula.PskTransitionalSending,
|
||||
},
|
||||
{
|
||||
name: "transitional-sending to enforced",
|
||||
myPskMode: nebula.PskTransitionalSending,
|
||||
theirPskMode: nebula.PskEnforced,
|
||||
},
|
||||
|
||||
// All enforced
|
||||
{
|
||||
name: "both enforced",
|
||||
myPskMode: nebula.PskEnforced,
|
||||
theirPskMode: nebula.PskEnforced,
|
||||
},
|
||||
|
||||
// Enforced can technically handshake with a traditional-accepting but it is bad to be in this state
|
||||
{
|
||||
name: "enforced to traditional-accepting",
|
||||
myPskMode: nebula.PskEnforced,
|
||||
theirPskMode: nebula.PskTransitionalAccepting,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
@ -230,8 +262,10 @@ func TestPSK(t *testing.T) {
|
|||
switch test.myPskMode {
|
||||
case nebula.PskNone:
|
||||
myPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "none"}}}
|
||||
case nebula.PskTransitional:
|
||||
myPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "transitional", "keys": []string{"this is a key"}}}}
|
||||
case nebula.PskTransitionalAccepting:
|
||||
myPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "transitional-accepting", "keys": []string{"this is a key"}}}}
|
||||
case nebula.PskTransitionalSending:
|
||||
myPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "transitional-sending", "keys": []string{"this is a key"}}}}
|
||||
case nebula.PskEnforced:
|
||||
myPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "enforced", "keys": []string{"this is a key"}}}}
|
||||
}
|
||||
|
@ -239,8 +273,10 @@ func TestPSK(t *testing.T) {
|
|||
switch test.theirPskMode {
|
||||
case nebula.PskNone:
|
||||
theirPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "none"}}}
|
||||
case nebula.PskTransitional:
|
||||
theirPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "transitional", "keys": []string{"this is a key"}}}}
|
||||
case nebula.PskTransitionalAccepting:
|
||||
theirPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "transitional-accepting", "keys": []string{"this is a key"}}}}
|
||||
case nebula.PskTransitionalSending:
|
||||
theirPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "transitional-sending", "keys": []string{"this is a key"}}}}
|
||||
case nebula.PskEnforced:
|
||||
theirPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "enforced", "keys": []string{"this is a key"}}}}
|
||||
}
|
||||
|
@ -265,10 +301,12 @@ func TestPSK(t *testing.T) {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
// If this is the stage 1 handshake packet and I am configured to enforce psk, my cert name should not appear.
|
||||
// It would likely be more obvious to unmarshal the payload
|
||||
if test.myPskMode == nebula.PskEnforced && h.Type == 0 && h.MessageCounter == 1 {
|
||||
assert.NotContains(t, string(p.Data), "test me")
|
||||
// If this is the stage 1 handshake packet and I am configured to send with a psk, my cert name should
|
||||
// not appear. It would likely be more obvious to unmarshal the payload and check but this works fine for now
|
||||
if test.myPskMode == nebula.PskEnforced || test.myPskMode == nebula.PskTransitionalSending {
|
||||
if h.Type == 0 && h.MessageCounter == 1 {
|
||||
assert.NotContains(t, string(p.Data), "test me")
|
||||
}
|
||||
}
|
||||
|
||||
if p.ToIp.Equal(theirUdpAddr.IP) && p.ToPort == uint16(theirUdpAddr.Port) && h.Type == 1 {
|
||||
|
|
|
@ -228,19 +228,22 @@ handshakes:
|
|||
#trigger_buffer: 64
|
||||
|
||||
# pki can be used to mask the contents of handshakes and makes handshaking with unintended recipients more difficult
|
||||
# all settings respond to a reload
|
||||
psk:
|
||||
# mode defines the how pre shared keys can be used in a handshake
|
||||
# `none` (the default) does not send or receive using a psk. Ideally `enforced` is used.
|
||||
# `transitional` can receive handshakes using a psk that we know about, but we will not send any handshakes using a psk.
|
||||
# This is helpful for transitioning to `enforced` and should be changed to `enforced` as soon as possible.
|
||||
# Move every node in your mesh to `transitional` then you can move every node in your mesh to `enforced` without having to stop the world
|
||||
# This assumes `keys` is the same on every node in your mesh
|
||||
# `enforced` enforces the use of a psk for all tunnels. Any node not also using `enforced` or `transitional` will not be able to handshake with us
|
||||
# `none` (the default) does not send or receive using a psk. Ideally `enforced` is used
|
||||
# `transitional-accepting` will send handshakes without using a psk and can receive handshakes using a psk we know about
|
||||
# `transitional-sending` will send handshakes using a psk but will still accept handshakes without them
|
||||
# `enforced` enforces the use of a psk for all tunnels. Any node not also using `enforced` or `transitional-sending` can not handshake with us
|
||||
#
|
||||
# When moving from `none` to `enforced` you will want to change every node in the mesh to `transitional-accepting` and reload
|
||||
# then move every node to `transitional-sending` then reload, and finally `enforced` then reload. This allows you to
|
||||
# avoid stopping the world to use psk. You must ensure at `transitional-accepting` that all nodes have the same psks.
|
||||
#mode: none
|
||||
|
||||
# In `transitional` and `enforced` modes, the keys provided here are sent through hkdf with the intended recipients
|
||||
# ip used in the info section. This helps guard against handshaking with the wrong host if your static_host_map or
|
||||
# lighthouse(s) has incorrect information.
|
||||
# In `transitional-accepting`, `transitional-sending` and `enforced` modes, the keys provided here are sent through
|
||||
# hkdf with the intended recipients ip used in the info section. This helps guard against handshaking with the wrong
|
||||
# host if your static_host_map or lighthouse(s) has incorrect information.
|
||||
#
|
||||
# Setting keys if mode is `none` has no effect.
|
||||
#
|
||||
|
|
25
psk.go
25
psk.go
|
@ -24,8 +24,10 @@ func (p PskMode) String() string {
|
|||
switch p {
|
||||
case PskNone:
|
||||
return "none"
|
||||
case PskTransitional:
|
||||
return "transitional"
|
||||
case PskTransitionalAccepting:
|
||||
return "transitional-accepting"
|
||||
case PskTransitionalSending:
|
||||
return "transitional-sending"
|
||||
case PskEnforced:
|
||||
return "enforced"
|
||||
}
|
||||
|
@ -37,8 +39,10 @@ func NewPskMode(m string) (PskMode, error) {
|
|||
switch m {
|
||||
case "none":
|
||||
return PskNone, nil
|
||||
case "transitional":
|
||||
return PskTransitional, nil
|
||||
case "transitional-accepting":
|
||||
return PskTransitionalAccepting, nil
|
||||
case "transitional-sending":
|
||||
return PskTransitionalSending, nil
|
||||
case "enforced":
|
||||
return PskEnforced, nil
|
||||
}
|
||||
|
@ -46,9 +50,10 @@ func NewPskMode(m string) (PskMode, error) {
|
|||
}
|
||||
|
||||
const (
|
||||
PskNone PskMode = 0
|
||||
PskTransitional PskMode = 1
|
||||
PskEnforced PskMode = 2
|
||||
PskNone PskMode = 0
|
||||
PskTransitionalAccepting PskMode = 1
|
||||
PskTransitionalSending PskMode = 2
|
||||
PskEnforced PskMode = 3
|
||||
)
|
||||
|
||||
type Psk struct {
|
||||
|
@ -102,7 +107,7 @@ func NewPsk(mode PskMode, keys []string, myVpnIp iputil.VpnIp) (*Psk, error) {
|
|||
// mixing in the intended recipients vpn ip and the result is returned.
|
||||
// If we are transitional or not using psks, an empty byte slice is returned
|
||||
func (p *Psk) MakeFor(vpnIp iputil.VpnIp) ([]byte, error) {
|
||||
if p.mode != PskEnforced {
|
||||
if p.mode == PskNone || p.mode == PskTransitionalAccepting {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
|
@ -129,7 +134,7 @@ func (p *Psk) cachePsks(myVpnIp iputil.VpnIp, keys []string) error {
|
|||
|
||||
p.Cache = [][]byte{}
|
||||
|
||||
if p.mode == PskTransitional {
|
||||
if p.mode == PskTransitionalAccepting || p.mode == PskTransitionalSending {
|
||||
// We are transitional, we accept empty psks
|
||||
p.Cache = append(p.Cache, nil)
|
||||
}
|
||||
|
@ -150,7 +155,7 @@ func (p *Psk) cachePsks(myVpnIp iputil.VpnIp, keys []string) error {
|
|||
// preparePrimaryKey if we are in enforced mode, will do an hkdf extract on the first key to benefit
|
||||
// outgoing handshake performance, MakeFor does the final expand step
|
||||
func (p *Psk) preparePrimaryKey(keys []string) error {
|
||||
if p.mode != PskEnforced {
|
||||
if p.mode == PskNone || p.mode == PskTransitionalAccepting {
|
||||
// If we aren't enforcing then there is nothing to prepare
|
||||
return nil
|
||||
}
|
||||
|
|
35
psk_test.go
35
psk_test.go
|
@ -20,16 +20,16 @@ func TestNewPsk(t *testing.T) {
|
|||
assert.Equal(t, []byte{}, b)
|
||||
})
|
||||
|
||||
t.Run("mode transitional", func(t *testing.T) {
|
||||
p, err := NewPsk(PskTransitional, nil, 1)
|
||||
t.Run("mode transitional-accepting", func(t *testing.T) {
|
||||
p, err := NewPsk(PskTransitionalAccepting, nil, 1)
|
||||
assert.Error(t, ErrNotEnoughPskKeys, err)
|
||||
|
||||
p, err = NewPsk(PskTransitional, []string{"1234567"}, 1)
|
||||
p, err = NewPsk(PskTransitionalAccepting, []string{"1234567"}, 1)
|
||||
assert.Error(t, ErrKeyTooShort)
|
||||
|
||||
p, err = NewPsk(PskTransitional, []string{"hi there friends"}, 1)
|
||||
p, err = NewPsk(PskTransitionalAccepting, []string{"hi there friends"}, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, PskTransitional, p.mode)
|
||||
assert.Equal(t, PskTransitionalAccepting, p.mode)
|
||||
assert.Empty(t, p.key)
|
||||
|
||||
assert.Len(t, p.Cache, 2)
|
||||
|
@ -42,6 +42,31 @@ func TestNewPsk(t *testing.T) {
|
|||
assert.Equal(t, []byte{}, b)
|
||||
})
|
||||
|
||||
t.Run("mode transitional-sending", func(t *testing.T) {
|
||||
p, err := NewPsk(PskTransitionalSending, nil, 1)
|
||||
assert.Error(t, ErrNotEnoughPskKeys, err)
|
||||
|
||||
p, err = NewPsk(PskTransitionalSending, []string{"1234567"}, 1)
|
||||
assert.Error(t, ErrKeyTooShort)
|
||||
|
||||
p, err = NewPsk(PskTransitionalSending, []string{"hi there friends"}, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, PskTransitionalSending, p.mode)
|
||||
|
||||
expectedKey := []byte{0x9c, 0x67, 0xab, 0x58, 0x79, 0x5c, 0x8a, 0xf0, 0xaa, 0xf0, 0x4c, 0x6c, 0x9a, 0x42, 0x6b, 0xe, 0xe2, 0x94, 0xb1, 0x0, 0x28, 0x1c, 0xdc, 0x88, 0x44, 0x35, 0x3f, 0xb7, 0xd5, 0x9, 0xc0, 0xda}
|
||||
assert.Equal(t, expectedKey, p.key)
|
||||
|
||||
assert.Len(t, p.Cache, 2)
|
||||
assert.Nil(t, p.Cache[0])
|
||||
|
||||
expectedCache := []byte{146, 120, 135, 31, 158, 102, 45, 189, 128, 190, 37, 101, 58, 254, 6, 166, 91, 209, 148, 131, 27, 193, 24, 25, 170, 65, 130, 189, 7, 179, 255, 17}
|
||||
assert.Equal(t, expectedCache, p.Cache[1])
|
||||
|
||||
expectedPsk := []byte{0xd9, 0x16, 0xa3, 0x66, 0x6a, 0x20, 0x26, 0xcf, 0x5d, 0x93, 0xad, 0xa3, 0x88, 0x2d, 0x57, 0xac, 0x9b, 0xc3, 0x5a, 0xb7, 0x8f, 0x6, 0x71, 0xc4, 0x3e, 0x5, 0x9e, 0xbc, 0x4e, 0xc8, 0x24, 0x17}
|
||||
b, err := p.MakeFor(0)
|
||||
assert.Equal(t, expectedPsk, b)
|
||||
})
|
||||
|
||||
t.Run("mode enforced", func(t *testing.T) {
|
||||
p, err := NewPsk(PskEnforced, nil, 1)
|
||||
assert.Error(t, ErrNotEnoughPskKeys, err)
|
||||
|
|
Loading…
Reference in New Issue