Browse code

Merge pull request #24349 from aaronlehmann/swarm-secrets-by-default

Generate a swarm joining secret if none is specified

Tõnis Tiigi authored on 2016/07/08 03:43:08
Showing 8 changed files
... ...
@@ -12,6 +12,13 @@ import (
12 12
 	"github.com/spf13/pflag"
13 13
 )
14 14
 
15
+const (
16
+	generatedSecretEntropyBytes = 16
17
+	generatedSecretBase         = 36
18
+	// floor(log(2^128-1, 36)) + 1
19
+	maxGeneratedSecretLength = 25
20
+)
21
+
15 22
 type initOptions struct {
16 23
 	swarmOptions
17 24
 	listenAddr      NodeAddrOption
... ...
@@ -46,6 +53,12 @@ func runInit(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts initOptions
46 46
 	client := dockerCli.Client()
47 47
 	ctx := context.Background()
48 48
 
49
+	// If no secret was specified, we create a random one
50
+	if !flags.Changed("secret") {
51
+		opts.secret = generateRandomSecret()
52
+		fmt.Fprintf(dockerCli.Out(), "No --secret provided. Generated random secret:\n\t%s\n\n", opts.secret)
53
+	}
54
+
49 55
 	req := swarm.InitRequest{
50 56
 		ListenAddr:      opts.listenAddr.String(),
51 57
 		ForceNewCluster: opts.forceNewCluster,
... ...
@@ -57,7 +70,26 @@ func runInit(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts initOptions
57 57
 		return err
58 58
 	}
59 59
 
60
-	fmt.Fprintf(dockerCli.Out(), "Swarm initialized: current node (%s) is now a manager.\n", nodeID)
60
+	fmt.Fprintf(dockerCli.Out(), "Swarm initialized: current node (%s) is now a manager.\n\n", nodeID)
61
+
62
+	// Fetch CAHash and Address from the API
63
+	info, err := client.Info(ctx)
64
+	if err != nil {
65
+		return err
66
+	}
67
+
68
+	node, _, err := client.NodeInspectWithRaw(ctx, nodeID)
69
+	if err != nil {
70
+		return err
71
+	}
72
+
73
+	if node.ManagerStatus != nil && info.Swarm.CACertHash != "" {
74
+		var secretArgs string
75
+		if opts.secret != "" {
76
+			secretArgs = "--secret " + opts.secret
77
+		}
78
+		fmt.Fprintf(dockerCli.Out(), "To add a worker to this swarm, run the following command:\n\tdocker swarm join %s \\\n\t--ca-hash %s \\\n\t%s\n", secretArgs, info.Swarm.CACertHash, node.ManagerStatus.Addr)
79
+	}
61 80
 
62 81
 	return nil
63 82
 }
... ...
@@ -237,7 +237,7 @@ func parseExternalCA(caSpec string) (*swarm.ExternalCA, error) {
237 237
 
238 238
 func addSwarmFlags(flags *pflag.FlagSet, opts *swarmOptions) {
239 239
 	flags.Var(&opts.autoAccept, flagAutoAccept, "Auto acceptance policy (worker, manager or none)")
240
-	flags.StringVar(&opts.secret, flagSecret, "", "Set secret value needed to accept nodes into cluster")
240
+	flags.StringVar(&opts.secret, flagSecret, "", "Set secret value needed to join a cluster")
241 241
 	flags.Int64Var(&opts.taskHistoryLimit, flagTaskHistoryLimit, 10, "Task history retention limit")
242 242
 	flags.DurationVar(&opts.dispatcherHeartbeat, flagDispatcherHeartbeat, time.Duration(5*time.Second), "Dispatcher heartbeat period")
243 243
 	flags.DurationVar(&opts.nodeCertExpiry, flagCertExpiry, time.Duration(90*24*time.Hour), "Validity period for node certificates")
244 244
new file mode 100644
... ...
@@ -0,0 +1,19 @@
0
+package swarm
1
+
2
+import (
3
+	cryptorand "crypto/rand"
4
+	"fmt"
5
+	"math/big"
6
+)
7
+
8
+func generateRandomSecret() string {
9
+	var secretBytes [generatedSecretEntropyBytes]byte
10
+
11
+	if _, err := cryptorand.Read(secretBytes[:]); err != nil {
12
+		panic(fmt.Errorf("failed to read random bytes: %v", err))
13
+	}
14
+
15
+	var nn big.Int
16
+	nn.SetBytes(secretBytes[:])
17
+	return fmt.Sprintf("%0[1]*s", maxGeneratedSecretLength, nn.Text(generatedSecretBase))
18
+}
... ...
@@ -29,12 +29,24 @@ in the newly created one node Swarm cluster.
29 29
 
30 30
 ```bash
31 31
 $ docker swarm init --listen-addr 192.168.99.121:2377
32
+No --secret provided. Generated random secret:
33
+	4ao565v9jsuogtq5t8s379ulb
34
+
32 35
 Swarm initialized: current node (1ujecd0j9n3ro9i6628smdmth) is now a manager.
36
+
37
+To add a worker to this swarm, run the following command:
38
+	docker swarm join --secret 4ao565v9jsuogtq5t8s379ulb \
39
+	--ca-hash sha256:07ce22bd1a7619f2adc0d63bd110479a170e7c4e69df05b67a1aa2705c88ef09 \
40
+	192.168.99.121:2377
33 41
 $ docker node ls
34 42
 ID                           HOSTNAME  MEMBERSHIP  STATUS  AVAILABILITY  MANAGER STATUS          LEADER
35 43
 1ujecd0j9n3ro9i6628smdmth *  manager1  Accepted    Ready   Active        Reachable               Yes
36 44
 ```
37 45
 
46
+If a secret for joining new nodes is not provided with `--secret`, `docker swarm init` will
47
+generate a random one and print it to the terminal (as seen in the example above). To initialize
48
+a swarm with no secret, use `--secret ""`.
49
+
38 50
 ### `--auto-accept value`
39 51
 
40 52
 This flag controls node acceptance into the cluster. By default, `worker` nodes are
... ...
@@ -47,7 +59,6 @@ For example, the following initializes a cluster with auto-acceptance of workers
47 47
 
48 48
 ```bash
49 49
 $ docker swarm init --listen-addr 192.168.99.121:2377 --auto-accept worker
50
-Swarm initialized: current node (1m8cdsylxbf3lk8qriqt07hx1) is now a manager.
51 50
 ```
52 51
 
53 52
 ### `--external-ca value`
... ...
@@ -27,7 +27,7 @@ targeted by this command becomes a `manager`. If it is not specified, it becomes
27 27
 ### Join a node to swarm as a manager
28 28
 
29 29
 ```bash
30
-$ docker swarm join --manager --listen-addr 192.168.99.122:2377 192.168.99.121:2377
30
+$ docker swarm join --secret 4ao565v9jsuogtq5t8s379ulb --manager --listen-addr 192.168.99.122:2377 192.168.99.121:2377
31 31
 This node joined a Swarm as a manager.
32 32
 $ docker node ls
33 33
 ID                           HOSTNAME  MEMBERSHIP  STATUS  AVAILABILITY  MANAGER STATUS         LEADER
... ...
@@ -38,7 +38,7 @@ dvfxp4zseq4s0rih1selh0d20    manager1  Accepted    Ready   Active        Reachab
38 38
 ### Join a node to swarm as a worker
39 39
 
40 40
 ```bash
41
-$ docker swarm join --listen-addr 192.168.99.123:2377 192.168.99.121:2377
41
+$ docker swarm join --secret 4ao565v9jsuogtq5t8s379ulb --listen-addr 192.168.99.123:2377 192.168.99.121:2377
42 42
 This node joined a Swarm as a worker.
43 43
 $ docker node ls
44 44
 ID                           HOSTNAME  MEMBERSHIP  STATUS  AVAILABILITY  MANAGER STATUS         LEADER
... ...
@@ -23,16 +23,17 @@ This tutorial uses the name `worker1`.
23 23
 the existing swarm:
24 24
 
25 25
     ```
26
-    docker swarm join <MANAGER-IP>:<PORT>
26
+    docker swarm join --secret <SECRET> <MANAGER-IP>:<PORT>
27 27
     ```
28 28
 
29
-    Replace `<MANAGER-IP>` with the address of the manager node and `<PORT>`
30
-    with the port where the manager listens.
29
+    Replace `<SECRET>` with the secret that was printed by `docker swarm init` in the
30
+    previous step. Replace `<MANAGER-IP>` with the address of the manager node
31
+    and `<PORT>` with the port where the manager listens.
31 32
 
32 33
     In the tutorial, the following command joins `worker1` to the swarm on `manager1`:
33 34
 
34 35
     ```
35
-    $ docker swarm join 192.168.99.100:2377
36
+    $ docker swarm join --secret 4ao565v9jsuogtq5t8s379ulb 192.168.99.100:2377
36 37
 
37 38
     This node joined a Swarm as a worker.
38 39
     ```
... ...
@@ -40,11 +41,12 @@ the existing swarm:
40 40
 3. Open a terminal and ssh into the machine where you want to run a second
41 41
 worker node. This tutorial uses the name `worker2`.
42 42
 
43
-4. Run `docker swarm join <MANAGER-IP>:<PORT>` to create a worker node joined to
43
+4. Run `docker swarm join --secret <SECRET> <MANAGER-IP>:<PORT>` to create a worker node joined to
44 44
 the existing Swarm.
45 45
 
46
-    Replace `<MANAGER-IP>` with the address of the manager node and `<PORT>`
47
-    with the port where the manager listens.
46
+    Replace `<SECRET>` with the secret that was printed by `docker swarm init` in the
47
+    previous step. Replace `<MANAGER-IP>` with the address of the manager node
48
+    and `<PORT>` with the port where the manager listens.
48 49
 
49 50
 5. Open a terminal and ssh into the machine where the manager node runs and run
50 51
 the `docker node ls` command to see the worker nodes:
... ...
@@ -30,8 +30,15 @@ node. For example, the tutorial uses a machine named `manager1`.
30 30
 
31 31
     ```
32 32
     $ docker swarm init --listen-addr 192.168.99.100:2377
33
+    No --secret provided. Generated random secret:
34
+	4ao565v9jsuogtq5t8s379ulb
33 35
 
34 36
     Swarm initialized: current node (dxn1zf6l61qsb1josjja83ngz) is now a manager.
37
+
38
+    To add a worker to this swarm, run the following command:
39
+	docker swarm join --secret 4ao565v9jsuogtq5t8s379ulb \
40
+	--ca-hash sha256:07ce22bd1a7619f2adc0d63bd110479a170e7c4e69df05b67a1aa2705c88ef09 \
41
+	192.168.99.100:2377
35 42
     ```
36 43
 
37 44
     The `--listen-addr` flag configures the manager node to listen on port
... ...
@@ -106,7 +106,7 @@ func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
106 106
 
107 107
 	c.Assert(d.Leave(true), checker.IsNil)
108 108
 
109
-	out, err = d.Cmd("swarm", "init", "--auto-accept", "none")
109
+	out, err = d.Cmd("swarm", "init", "--auto-accept", "none", "--secret", "")
110 110
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
111 111
 
112 112
 	spec = getSpec()