Browse code

Add some swarm encryption tests, and modify some existing ones, to test locking the whole cluster

Signed-off-by: cyli <cyli@twistedmatrix.com>

cyli authored on 2016/11/18 09:51:33
Showing 1 changed files
... ...
@@ -849,6 +849,12 @@ func (s *DockerSwarmSuite) TestDNSConfigUpdate(c *check.C) {
849 849
 	c.Assert(strings.TrimSpace(out), checker.Equals, "{[1.2.3.4] [example.com] [timeout:3]}")
850 850
 }
851 851
 
852
+func getNodeStatus(c *check.C, d *SwarmDaemon) swarm.LocalNodeState {
853
+	info, err := d.info()
854
+	c.Assert(err, checker.IsNil)
855
+	return info.LocalNodeState
856
+}
857
+
852 858
 func (s *DockerSwarmSuite) TestSwarmInitLocked(c *check.C) {
853 859
 	d := s.AddDaemon(c, false, false)
854 860
 
... ...
@@ -870,15 +876,11 @@ func (s *DockerSwarmSuite) TestSwarmInitLocked(c *check.C) {
870 870
 	outs, err = d.Cmd("swarm", "unlock-key", "-q")
871 871
 	c.Assert(outs, checker.Equals, unlockKey+"\n")
872 872
 
873
-	info, err := d.info()
874
-	c.Assert(err, checker.IsNil)
875
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
873
+	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
876 874
 
877 875
 	c.Assert(d.Restart(), checker.IsNil)
878 876
 
879
-	info, err = d.info()
880
-	c.Assert(err, checker.IsNil)
881
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateLocked)
877
+	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
882 878
 
883 879
 	cmd := d.command("swarm", "unlock")
884 880
 	cmd.Stdin = bytes.NewBufferString("wrong-secret-key")
... ...
@@ -886,14 +888,14 @@ func (s *DockerSwarmSuite) TestSwarmInitLocked(c *check.C) {
886 886
 	c.Assert(err, checker.NotNil, check.Commentf("out: %v", string(out)))
887 887
 	c.Assert(string(out), checker.Contains, "invalid key")
888 888
 
889
+	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
890
+
889 891
 	cmd = d.command("swarm", "unlock")
890 892
 	cmd.Stdin = bytes.NewBufferString(unlockKey)
891 893
 	out, err = cmd.CombinedOutput()
892 894
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
893 895
 
894
-	info, err = d.info()
895
-	c.Assert(err, checker.IsNil)
896
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
896
+	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
897 897
 
898 898
 	outs, err = d.Cmd("node", "ls")
899 899
 	c.Assert(err, checker.IsNil)
... ...
@@ -903,13 +905,22 @@ func (s *DockerSwarmSuite) TestSwarmInitLocked(c *check.C) {
903 903
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
904 904
 
905 905
 	// Wait for autolock to be turned off
906
-	time.Sleep(time.Second)
907
-
908 906
 	c.Assert(d.Restart(), checker.IsNil)
907
+	status := getNodeStatus(c, d)
908
+	if status == swarm.LocalNodeStateLocked {
909
+		// it must not have updated in time - unlock, wait 3 seconds, and try again
910
+		cmd := d.command("swarm", "unlock")
911
+		cmd.Stdin = bytes.NewBufferString(unlockKey)
912
+		out, err := cmd.CombinedOutput()
913
+		c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
909 914
 
910
-	info, err = d.info()
911
-	c.Assert(err, checker.IsNil)
912
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
915
+		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
916
+
917
+		time.Sleep(3 * time.Second)
918
+		c.Assert(d.Restart(), checker.IsNil)
919
+	}
920
+
921
+	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
913 922
 
914 923
 	outs, err = d.Cmd("node", "ls")
915 924
 	c.Assert(err, checker.IsNil)
... ...
@@ -946,6 +957,191 @@ func (s *DockerSwarmSuite) TestSwarmLeaveLocked(c *check.C) {
946 946
 	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
947 947
 }
948 948
 
949
+func (s *DockerSwarmSuite) TestSwarmLockUnlockCluster(c *check.C) {
950
+	d1 := s.AddDaemon(c, true, true)
951
+	d2 := s.AddDaemon(c, true, true)
952
+	d3 := s.AddDaemon(c, true, true)
953
+
954
+	// they start off unlocked
955
+	c.Assert(d2.Restart(), checker.IsNil)
956
+	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
957
+
958
+	// stop this one so it does not get autolock info
959
+	c.Assert(d2.Stop(), checker.IsNil)
960
+
961
+	// enable autolock
962
+	outs, err := d1.Cmd("swarm", "update", "--autolock")
963
+	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
964
+
965
+	c.Assert(outs, checker.Contains, "docker swarm unlock")
966
+
967
+	var unlockKey string
968
+	for _, line := range strings.Split(outs, "\n") {
969
+		if strings.Contains(line, "SWMKEY") {
970
+			unlockKey = strings.TrimSpace(line)
971
+			break
972
+		}
973
+	}
974
+
975
+	c.Assert(unlockKey, checker.Not(checker.Equals), "")
976
+
977
+	outs, err = d1.Cmd("swarm", "unlock-key", "-q")
978
+	c.Assert(outs, checker.Equals, unlockKey+"\n")
979
+
980
+	// The ones that got the cluster update should be set to locked
981
+	for _, d := range []*SwarmDaemon{d1, d3} {
982
+		c.Assert(d.Restart(), checker.IsNil)
983
+		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
984
+
985
+		cmd := d.command("swarm", "unlock")
986
+		cmd.Stdin = bytes.NewBufferString(unlockKey)
987
+		out, err := cmd.CombinedOutput()
988
+		c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
989
+		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
990
+	}
991
+
992
+	// d2 never got the cluster update, so it is still set to unlocked
993
+	c.Assert(d2.Start(), checker.IsNil)
994
+	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
995
+
996
+	// d2 is now set to lock
997
+	c.Assert(d2.Restart(), checker.IsNil)
998
+	status := getNodeStatus(c, d2)
999
+	if status == swarm.LocalNodeStateActive {
1000
+		// it must not have updated in time - wait 3 seconds, and try again
1001
+		time.Sleep(3 * time.Second)
1002
+		c.Assert(d2.Restart(), checker.IsNil)
1003
+	}
1004
+	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateLocked)
1005
+
1006
+	// leave it locked, and set the cluster to no longer autolock
1007
+	outs, err = d1.Cmd("swarm", "update", "--autolock=false")
1008
+	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
1009
+
1010
+	// the ones that got the update are now set to unlocked
1011
+	for _, d := range []*SwarmDaemon{d1, d3} {
1012
+		c.Assert(d.Restart(), checker.IsNil)
1013
+		status := getNodeStatus(c, d)
1014
+		if status == swarm.LocalNodeStateLocked {
1015
+			// it must not have updated to be unlocked in time - unlock, wait 3 seconds, and try again
1016
+			cmd := d.command("swarm", "unlock")
1017
+			cmd.Stdin = bytes.NewBufferString(unlockKey)
1018
+			out, err := cmd.CombinedOutput()
1019
+			c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
1020
+
1021
+			c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
1022
+
1023
+			time.Sleep(3 * time.Second)
1024
+			c.Assert(d.Restart(), checker.IsNil)
1025
+		}
1026
+
1027
+		c.Assert(d.Restart(), checker.IsNil)
1028
+		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
1029
+	}
1030
+
1031
+	// d2 still locked
1032
+	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateLocked)
1033
+
1034
+	// unlock it
1035
+	cmd := d2.command("swarm", "unlock")
1036
+	cmd.Stdin = bytes.NewBufferString(unlockKey)
1037
+	out, err := cmd.CombinedOutput()
1038
+	c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
1039
+	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
1040
+
1041
+	// once it's caught up, d2 is set to not be locked
1042
+	c.Assert(d2.Restart(), checker.IsNil)
1043
+	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
1044
+
1045
+	// managers who join now are also unlocked
1046
+	d4 := s.AddDaemon(c, true, true)
1047
+	c.Assert(d4.Restart(), checker.IsNil)
1048
+	c.Assert(getNodeStatus(c, d4), checker.Equals, swarm.LocalNodeStateActive)
1049
+}
1050
+
1051
+func (s *DockerSwarmSuite) TestSwarmJoinPromoteLocked(c *check.C) {
1052
+	d1 := s.AddDaemon(c, true, true)
1053
+
1054
+	// enable autolock
1055
+	outs, err := d1.Cmd("swarm", "update", "--autolock")
1056
+	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
1057
+
1058
+	c.Assert(outs, checker.Contains, "docker swarm unlock")
1059
+
1060
+	var unlockKey string
1061
+	for _, line := range strings.Split(outs, "\n") {
1062
+		if strings.Contains(line, "SWMKEY") {
1063
+			unlockKey = strings.TrimSpace(line)
1064
+			break
1065
+		}
1066
+	}
1067
+
1068
+	c.Assert(unlockKey, checker.Not(checker.Equals), "")
1069
+
1070
+	outs, err = d1.Cmd("swarm", "unlock-key", "-q")
1071
+	c.Assert(outs, checker.Equals, unlockKey+"\n")
1072
+
1073
+	// joined workers start off unlocked
1074
+	d2 := s.AddDaemon(c, true, false)
1075
+	c.Assert(d2.Restart(), checker.IsNil)
1076
+	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
1077
+
1078
+	// promote worker
1079
+	outs, err = d1.Cmd("node", "promote", d2.Info.NodeID)
1080
+	c.Assert(err, checker.IsNil)
1081
+	c.Assert(outs, checker.Contains, "promoted to a manager in the swarm")
1082
+
1083
+	// join new manager node
1084
+	d3 := s.AddDaemon(c, true, true)
1085
+
1086
+	// both new nodes are locked
1087
+	for _, d := range []*SwarmDaemon{d2, d3} {
1088
+		c.Assert(d.Restart(), checker.IsNil)
1089
+		status := getNodeStatus(c, d)
1090
+		if status == swarm.LocalNodeStateActive {
1091
+			// it must not have updated in time - wait 3 seconds, and try again
1092
+			time.Sleep(3 * time.Second)
1093
+			c.Assert(d.Restart(), checker.IsNil)
1094
+		}
1095
+		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
1096
+
1097
+		cmd := d.command("swarm", "unlock")
1098
+		cmd.Stdin = bytes.NewBufferString(unlockKey)
1099
+		out, err := cmd.CombinedOutput()
1100
+		c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
1101
+		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
1102
+	}
1103
+
1104
+	// demote manager back to worker - workers are not locked
1105
+	outs, err = d1.Cmd("node", "demote", d3.Info.NodeID)
1106
+	c.Assert(err, checker.IsNil)
1107
+	c.Assert(outs, checker.Contains, "demoted in the swarm")
1108
+
1109
+	// verify that it's been demoted
1110
+	out, err := d1.Cmd("node", "ls", "--filter", "id="+d3.Info.NodeID)
1111
+	c.Assert(err, checker.IsNil)
1112
+	lines := strings.Split(strings.TrimSpace(out), "\n")
1113
+	c.Assert(len(lines), checker.GreaterThan, 0)
1114
+	columns := strings.Fields(lines[len(lines)-1])
1115
+	c.Assert(columns, checker.HasLen, 4) // if it was a manager it'd have a manager status field
1116
+
1117
+	c.Assert(d3.Restart(), checker.IsNil)
1118
+	status := getNodeStatus(c, d3)
1119
+	if status == swarm.LocalNodeStateLocked {
1120
+		// it must not have updated in time - unlock, wait 3 seconds, and try again
1121
+		cmd := d3.command("swarm", "unlock")
1122
+		cmd.Stdin = bytes.NewBufferString(unlockKey)
1123
+		out, err := cmd.CombinedOutput()
1124
+		c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
1125
+
1126
+		c.Assert(getNodeStatus(c, d3), checker.Equals, swarm.LocalNodeStateActive)
1127
+
1128
+		time.Sleep(3 * time.Second)
1129
+		c.Assert(d3.Restart(), checker.IsNil)
1130
+	}
1131
+	c.Assert(getNodeStatus(c, d3), checker.Equals, swarm.LocalNodeStateActive)
1132
+}
1133
+
949 1134
 func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) {
950 1135
 	d := s.AddDaemon(c, true, true)
951 1136
 
... ...
@@ -977,10 +1173,7 @@ func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) {
977 977
 		c.Assert(newUnlockKey, checker.Not(checker.Equals), unlockKey)
978 978
 
979 979
 		c.Assert(d.Restart(), checker.IsNil)
980
-
981
-		info, err := d.info()
982
-		c.Assert(err, checker.IsNil)
983
-		c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateLocked)
980
+		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
984 981
 
985 982
 		outs, _ = d.Cmd("node", "ls")
986 983
 		c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
... ...
@@ -1017,9 +1210,7 @@ func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) {
1017 1017
 		out, err = cmd.CombinedOutput()
1018 1018
 		c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
1019 1019
 
1020
-		info, err = d.info()
1021
-		c.Assert(err, checker.IsNil)
1022
-		c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
1020
+		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
1023 1021
 
1024 1022
 		outs, err = d.Cmd("node", "ls")
1025 1023
 		c.Assert(err, checker.IsNil)
... ...
@@ -1029,6 +1220,151 @@ func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) {
1029 1029
 	}
1030 1030
 }
1031 1031
 
1032
+// This differs from `TestSwarmRotateUnlockKey` because that one rotates a single node, which is the leader.
1033
+// This one keeps the leader up, and asserts that other manager nodes in the cluster also have their unlock
1034
+// key rotated.
1035
+func (s *DockerSwarmSuite) TestSwarmClusterRotateUnlockKey(c *check.C) {
1036
+	d1 := s.AddDaemon(c, true, true) // leader - don't restart this one, we don't want leader election delays
1037
+	d2 := s.AddDaemon(c, true, true)
1038
+	d3 := s.AddDaemon(c, true, true)
1039
+
1040
+	outs, err := d1.Cmd("swarm", "update", "--autolock")
1041
+	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
1042
+
1043
+	c.Assert(outs, checker.Contains, "docker swarm unlock")
1044
+
1045
+	var unlockKey string
1046
+	for _, line := range strings.Split(outs, "\n") {
1047
+		if strings.Contains(line, "SWMKEY") {
1048
+			unlockKey = strings.TrimSpace(line)
1049
+			break
1050
+		}
1051
+	}
1052
+
1053
+	c.Assert(unlockKey, checker.Not(checker.Equals), "")
1054
+
1055
+	outs, err = d1.Cmd("swarm", "unlock-key", "-q")
1056
+	c.Assert(outs, checker.Equals, unlockKey+"\n")
1057
+
1058
+	// Rotate multiple times
1059
+	for i := 0; i != 3; i++ {
1060
+		outs, err = d1.Cmd("swarm", "unlock-key", "-q", "--rotate")
1061
+		c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
1062
+		// Strip \n
1063
+		newUnlockKey := outs[:len(outs)-1]
1064
+		c.Assert(newUnlockKey, checker.Not(checker.Equals), "")
1065
+		c.Assert(newUnlockKey, checker.Not(checker.Equals), unlockKey)
1066
+
1067
+		c.Assert(d2.Restart(), checker.IsNil)
1068
+		c.Assert(d3.Restart(), checker.IsNil)
1069
+
1070
+		for _, d := range []*SwarmDaemon{d2, d3} {
1071
+			c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
1072
+
1073
+			outs, _ := d.Cmd("node", "ls")
1074
+			c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
1075
+
1076
+			cmd := d.command("swarm", "unlock")
1077
+			cmd.Stdin = bytes.NewBufferString(unlockKey)
1078
+			out, err := cmd.CombinedOutput()
1079
+
1080
+			if err == nil {
1081
+				// On occasion, the daemon may not have finished
1082
+				// rotating the KEK before restarting. The test is
1083
+				// intentionally written to explore this behavior.
1084
+				// When this happens, unlocking with the old key will
1085
+				// succeed. If we wait for the rotation to happen and
1086
+				// restart again, the new key should be required this
1087
+				// time.
1088
+
1089
+				time.Sleep(3 * time.Second)
1090
+
1091
+				c.Assert(d.Restart(), checker.IsNil)
1092
+
1093
+				cmd = d.command("swarm", "unlock")
1094
+				cmd.Stdin = bytes.NewBufferString(unlockKey)
1095
+				out, err = cmd.CombinedOutput()
1096
+			}
1097
+			c.Assert(err, checker.NotNil, check.Commentf("out: %v", string(out)))
1098
+			c.Assert(string(out), checker.Contains, "invalid key")
1099
+
1100
+			outs, _ = d.Cmd("node", "ls")
1101
+			c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
1102
+
1103
+			cmd = d.command("swarm", "unlock")
1104
+			cmd.Stdin = bytes.NewBufferString(newUnlockKey)
1105
+			out, err = cmd.CombinedOutput()
1106
+			c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
1107
+
1108
+			c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
1109
+
1110
+			outs, err = d.Cmd("node", "ls")
1111
+			c.Assert(err, checker.IsNil)
1112
+			c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
1113
+		}
1114
+
1115
+		unlockKey = newUnlockKey
1116
+	}
1117
+}
1118
+
1119
+func (s *DockerSwarmSuite) TestSwarmAlternateLockUnlock(c *check.C) {
1120
+	d := s.AddDaemon(c, true, true)
1121
+
1122
+	var unlockKey string
1123
+	for i := 0; i < 2; i++ {
1124
+		// set to lock
1125
+		outs, err := d.Cmd("swarm", "update", "--autolock")
1126
+		c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
1127
+		c.Assert(outs, checker.Contains, "docker swarm unlock")
1128
+
1129
+		for _, line := range strings.Split(outs, "\n") {
1130
+			if strings.Contains(line, "SWMKEY") {
1131
+				unlockKey = strings.TrimSpace(line)
1132
+				break
1133
+			}
1134
+		}
1135
+
1136
+		c.Assert(unlockKey, checker.Not(checker.Equals), "")
1137
+
1138
+		c.Assert(d.Restart(), checker.IsNil)
1139
+		status := getNodeStatus(c, d)
1140
+		if status == swarm.LocalNodeStateActive {
1141
+			// hasn't updated yet - wait and try again
1142
+			time.Sleep(3 * time.Second)
1143
+			c.Assert(d.Restart(), checker.IsNil)
1144
+			status = getNodeStatus(c, d)
1145
+		}
1146
+		c.Assert(status, checker.Equals, swarm.LocalNodeStateLocked)
1147
+
1148
+		cmd := d.command("swarm", "unlock")
1149
+		cmd.Stdin = bytes.NewBufferString(unlockKey)
1150
+		out, err := cmd.CombinedOutput()
1151
+		c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
1152
+
1153
+		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
1154
+
1155
+		outs, err = d.Cmd("swarm", "update", "--autolock=false")
1156
+		c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
1157
+
1158
+		c.Assert(d.Restart(), checker.IsNil)
1159
+		status = getNodeStatus(c, d)
1160
+		if status == swarm.LocalNodeStateLocked {
1161
+			// it must not have updated to be unlocked in time - unlock, wait 3 seconds, and try again
1162
+			cmd := d.command("swarm", "unlock")
1163
+			cmd.Stdin = bytes.NewBufferString(unlockKey)
1164
+			out, err := cmd.CombinedOutput()
1165
+			c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
1166
+
1167
+			c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
1168
+
1169
+			time.Sleep(3 * time.Second)
1170
+			c.Assert(d.Restart(), checker.IsNil)
1171
+			status = getNodeStatus(c, d)
1172
+		}
1173
+		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
1174
+	}
1175
+}
1176
+
1032 1177
 func (s *DockerSwarmSuite) TestExtraHosts(c *check.C) {
1033 1178
 	d := s.AddDaemon(c, true, true)
1034 1179