3
0
Эх сурвалжийг харах

Add an additional transitional mode to get us to enforced safely

Nate Brown 4 жил өмнө
parent
commit
ba8646fa83
4 өөрчлөгдсөн 114 нэмэгдсэн , 43 устгасан
  1. 57 19
      e2e/handshakes_test.go
  2. 12 9
      examples/config.yml
  3. 15 10
      psk.go
  4. 30 5
      psk_test.go

+ 57 - 19
e2e/handshakes_test.go

@@ -190,37 +190,69 @@ func TestPSK(t *testing.T) {
 		myPskMode    nebula.PskMode
 		myPskMode    nebula.PskMode
 		theirPskMode nebula.PskMode
 		theirPskMode nebula.PskMode
 	}{
 	}{
+		// None and transitional-accepting both ways
 		{
 		{
-			name:         "none to transitional",
+			name:         "none to transitional-accepting",
 			myPskMode:    nebula.PskNone,
 			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,
 			theirPskMode: nebula.PskNone,
 		},
 		},
+
+		// All transitional-accepting
+		{
+			name:         "both transitional-accepting",
+			myPskMode:    nebula.PskTransitionalAccepting,
+			theirPskMode: nebula.PskTransitionalAccepting,
+		},
+
+		// transitional-accepting and transitional-sending both ways
+		{
+			name:         "transitional-accepting to transitional-sending",
+			myPskMode:    nebula.PskTransitionalAccepting,
+			theirPskMode: nebula.PskTransitionalSending,
+		},
+		{
+			name:         "transitional-sending to transitional-accepting",
+			myPskMode:    nebula.PskTransitionalSending,
+			theirPskMode: nebula.PskTransitionalAccepting,
+		},
+
+		// All transitional-sending
 		{
 		{
-			name:         "both transitional",
-			myPskMode:    nebula.PskTransitional,
-			theirPskMode: nebula.PskTransitional,
+			name:         "transitional-sending to transitional-sending",
+			myPskMode:    nebula.PskTransitionalSending,
+			theirPskMode: nebula.PskTransitionalSending,
 		},
 		},
 
 
+		// enforced and transitional-sending both ways
 		{
 		{
-			name:         "enforced to transitional",
+			name:         "enforced to transitional-sending",
 			myPskMode:    nebula.PskEnforced,
 			myPskMode:    nebula.PskEnforced,
-			theirPskMode: nebula.PskTransitional,
+			theirPskMode: nebula.PskTransitionalSending,
 		},
 		},
 		{
 		{
-			name:         "transitional to enforced",
-			myPskMode:    nebula.PskTransitional,
+			name:         "transitional-sending to enforced",
+			myPskMode:    nebula.PskTransitionalSending,
 			theirPskMode: nebula.PskEnforced,
 			theirPskMode: nebula.PskEnforced,
 		},
 		},
+
+		// All enforced
 		{
 		{
 			name:         "both enforced",
 			name:         "both enforced",
 			myPskMode:    nebula.PskEnforced,
 			myPskMode:    nebula.PskEnforced,
 			theirPskMode: 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 {
 	for _, test := range tests {
@@ -230,8 +262,10 @@ func TestPSK(t *testing.T) {
 			switch test.myPskMode {
 			switch test.myPskMode {
 			case nebula.PskNone:
 			case nebula.PskNone:
 				myPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "none"}}}
 				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:
 			case nebula.PskEnforced:
 				myPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "enforced", "keys": []string{"this is a key"}}}}
 				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 {
 			switch test.theirPskMode {
 			case nebula.PskNone:
 			case nebula.PskNone:
 				theirPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "none"}}}
 				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:
 			case nebula.PskEnforced:
 				theirPskSettings = &m{"handshakes": &m{"psk": &m{"mode": "enforced", "keys": []string{"this is a key"}}}}
 				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)
 					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 {
 				if p.ToIp.Equal(theirUdpAddr.IP) && p.ToPort == uint16(theirUdpAddr.Port) && h.Type == 1 {

+ 12 - 9
examples/config.yml

@@ -228,19 +228,22 @@ handshakes:
   #trigger_buffer: 64
   #trigger_buffer: 64
 
 
   # pki can be used to mask the contents of handshakes and makes handshaking with unintended recipients more difficult
   # 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:
   psk:
     # mode defines the how pre shared keys can be used in a handshake
     # 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
     #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.
     # Setting keys if mode is `none` has no effect.
     #
     #

+ 15 - 10
psk.go

@@ -24,8 +24,10 @@ func (p PskMode) String() string {
 	switch p {
 	switch p {
 	case PskNone:
 	case PskNone:
 		return "none"
 		return "none"
-	case PskTransitional:
-		return "transitional"
+	case PskTransitionalAccepting:
+		return "transitional-accepting"
+	case PskTransitionalSending:
+		return "transitional-sending"
 	case PskEnforced:
 	case PskEnforced:
 		return "enforced"
 		return "enforced"
 	}
 	}
@@ -37,8 +39,10 @@ func NewPskMode(m string) (PskMode, error) {
 	switch m {
 	switch m {
 	case "none":
 	case "none":
 		return PskNone, nil
 		return PskNone, nil
-	case "transitional":
-		return PskTransitional, nil
+	case "transitional-accepting":
+		return PskTransitionalAccepting, nil
+	case "transitional-sending":
+		return PskTransitionalSending, nil
 	case "enforced":
 	case "enforced":
 		return PskEnforced, nil
 		return PskEnforced, nil
 	}
 	}
@@ -46,9 +50,10 @@ func NewPskMode(m string) (PskMode, error) {
 }
 }
 
 
 const (
 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 {
 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.
 // 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
 // If we are transitional or not using psks, an empty byte slice is returned
 func (p *Psk) MakeFor(vpnIp iputil.VpnIp) ([]byte, error) {
 func (p *Psk) MakeFor(vpnIp iputil.VpnIp) ([]byte, error) {
-	if p.mode != PskEnforced {
+	if p.mode == PskNone || p.mode == PskTransitionalAccepting {
 		return []byte{}, nil
 		return []byte{}, nil
 	}
 	}
 
 
@@ -129,7 +134,7 @@ func (p *Psk) cachePsks(myVpnIp iputil.VpnIp, keys []string) error {
 
 
 	p.Cache = [][]byte{}
 	p.Cache = [][]byte{}
 
 
-	if p.mode == PskTransitional {
+	if p.mode == PskTransitionalAccepting || p.mode == PskTransitionalSending {
 		// We are transitional, we accept empty psks
 		// We are transitional, we accept empty psks
 		p.Cache = append(p.Cache, nil)
 		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
 // 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
 // outgoing handshake performance, MakeFor does the final expand step
 func (p *Psk) preparePrimaryKey(keys []string) error {
 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
 		// If we aren't enforcing then there is nothing to prepare
 		return nil
 		return nil
 	}
 	}

+ 30 - 5
psk_test.go

@@ -20,16 +20,16 @@ func TestNewPsk(t *testing.T) {
 		assert.Equal(t, []byte{}, b)
 		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)
 		assert.Error(t, ErrNotEnoughPskKeys, err)
 
 
-		p, err = NewPsk(PskTransitional, []string{"1234567"}, 1)
+		p, err = NewPsk(PskTransitionalAccepting, []string{"1234567"}, 1)
 		assert.Error(t, ErrKeyTooShort)
 		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.NoError(t, err)
-		assert.Equal(t, PskTransitional, p.mode)
+		assert.Equal(t, PskTransitionalAccepting, p.mode)
 		assert.Empty(t, p.key)
 		assert.Empty(t, p.key)
 
 
 		assert.Len(t, p.Cache, 2)
 		assert.Len(t, p.Cache, 2)
@@ -42,6 +42,31 @@ func TestNewPsk(t *testing.T) {
 		assert.Equal(t, []byte{}, b)
 		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) {
 	t.Run("mode enforced", func(t *testing.T) {
 		p, err := NewPsk(PskEnforced, nil, 1)
 		p, err := NewPsk(PskEnforced, nil, 1)
 		assert.Error(t, ErrNotEnoughPskKeys, err)
 		assert.Error(t, ErrNotEnoughPskKeys, err)