Browse code

Add API test to rotate the swarm CA certificate

Signed-off-by: Ying Li <ying.li@docker.com>

Ying Li authored on 2017/05/09 09:14:34
Showing 1 changed files
... ...
@@ -14,12 +14,15 @@ import (
14 14
 	"sync"
15 15
 	"time"
16 16
 
17
+	"github.com/cloudflare/cfssl/csr"
17 18
 	"github.com/cloudflare/cfssl/helpers"
19
+	"github.com/cloudflare/cfssl/initca"
18 20
 	"github.com/docker/docker/api/types"
19 21
 	"github.com/docker/docker/api/types/container"
20 22
 	"github.com/docker/docker/api/types/swarm"
21 23
 	"github.com/docker/docker/integration-cli/checker"
22 24
 	"github.com/docker/docker/integration-cli/daemon"
25
+	"github.com/docker/swarmkit/ca"
23 26
 	"github.com/go-check/check"
24 27
 )
25 28
 
... ...
@@ -930,3 +933,72 @@ func (s *DockerSwarmSuite) TestAPISwarmHealthcheckNone(c *check.C) {
930 930
 	out, err = d.Cmd("exec", containers[0], "ping", "-c1", "-W3", "top")
931 931
 	c.Assert(err, checker.IsNil, check.Commentf(out))
932 932
 }
933
+
934
+func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *check.C) {
935
+	m := s.AddDaemon(c, true, true)
936
+	w := s.AddDaemon(c, true, false)
937
+
938
+	info, err := m.SwarmInfo()
939
+	c.Assert(err, checker.IsNil)
940
+
941
+	currentTrustRoot := info.Cluster.TLSInfo.TrustRoot
942
+
943
+	// rotate multiple times
944
+	for i := 0; i < 4; i++ {
945
+		var cert, key []byte
946
+		if i%2 != 0 {
947
+			cert, _, key, err = initca.New(&csr.CertificateRequest{
948
+				CN:         "newRoot",
949
+				KeyRequest: csr.NewBasicKeyRequest(),
950
+				CA:         &csr.CAConfig{Expiry: ca.RootCAExpiration},
951
+			})
952
+			c.Assert(err, checker.IsNil)
953
+		}
954
+		expectedCert := string(cert)
955
+		m.UpdateSwarm(c, func(s *swarm.Spec) {
956
+			s.CAConfig.SigningCACert = expectedCert
957
+			s.CAConfig.SigningCAKey = string(key)
958
+			s.CAConfig.ForceRotate++
959
+		})
960
+
961
+		// poll to make sure update succeeds
962
+		var clusterTLSInfo swarm.TLSInfo
963
+		for j := 0; j < 18; j++ {
964
+			info, err := m.SwarmInfo()
965
+			c.Assert(err, checker.IsNil)
966
+			c.Assert(info.Cluster.Spec.CAConfig.SigningCACert, checker.Equals, expectedCert)
967
+			// the desired CA key is always redacted
968
+			c.Assert(info.Cluster.Spec.CAConfig.SigningCAKey, checker.Equals, "")
969
+
970
+			clusterTLSInfo = info.Cluster.TLSInfo
971
+
972
+			if !info.Cluster.RootRotationInProgress {
973
+				break
974
+			}
975
+
976
+			// root rotation not done
977
+			time.Sleep(250 * time.Millisecond)
978
+		}
979
+		c.Assert(clusterTLSInfo.TrustRoot, checker.Not(checker.Equals), currentTrustRoot)
980
+		if cert != nil {
981
+			c.Assert(clusterTLSInfo.TrustRoot, checker.Equals, expectedCert)
982
+		}
983
+		// could take another second or two for the nodes to trust the new roots after the've all gotten
984
+		// new TLS certificates
985
+		for j := 0; j < 18; j++ {
986
+			mInfo := m.GetNode(c, m.NodeID).Description.TLSInfo
987
+			wInfo := m.GetNode(c, w.NodeID).Description.TLSInfo
988
+
989
+			if mInfo.TrustRoot == clusterTLSInfo.TrustRoot && wInfo.TrustRoot == clusterTLSInfo.TrustRoot {
990
+				break
991
+			}
992
+
993
+			// nodes don't trust root certs yet
994
+			time.Sleep(250 * time.Millisecond)
995
+		}
996
+
997
+		c.Assert(m.GetNode(c, m.NodeID).Description.TLSInfo, checker.DeepEquals, clusterTLSInfo)
998
+		c.Assert(m.GetNode(c, w.NodeID).Description.TLSInfo, checker.DeepEquals, clusterTLSInfo)
999
+		currentTrustRoot = clusterTLSInfo.TrustRoot
1000
+	}
1001
+}