Signed-off-by: Ying Li <ying.li@docker.com>
| ... | ... |
@@ -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 |
+} |